Repository: jkuhnert/ognl Branch: main Commit: 46795d170421 Files: 336 Total size: 1.7 MB Directory structure: gitextract_sc8f7uqk/ ├── .claude/ │ └── settings.json ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── maven.yml │ ├── ognl-3-4-x.yml │ ├── performance-baseline.yml │ ├── performance.yml │ └── sonar.yml ├── .gitignore ├── .mvn/ │ └── wrapper/ │ ├── MavenWrapperDownloader.java │ └── maven-wrapper.properties ├── CLAUDE.md ├── KEYS ├── LICENSE.txt ├── README.md ├── SECURITY.md ├── benchmarks/ │ ├── etc/ │ │ └── ognl-runtime-benchmark-baseline.json │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── ognl/ │ ├── DefaultMemberAccess.java │ ├── Run.java │ └── benchmarks/ │ ├── OgnlPerformanceBenchmarks.java │ └── OgnlRuntimePerformanceBenchmarks.java ├── docs/ │ ├── DeveloperGuide.md │ ├── ISSUE_103_ANALYSIS.md │ ├── LanguageGuide.md │ ├── NullSafeOperator.md │ ├── VersionNotes.md │ └── plans/ │ ├── ISSUE_18_COMPILED_MODE_PARITY.md │ └── JDK25_FORWARD_COMPATIBILITY.md ├── mvnw ├── mvnw.cmd ├── ognl/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── ognl/ │ │ │ ├── ASTAdd.java │ │ │ ├── ASTAnd.java │ │ │ ├── ASTAssign.java │ │ │ ├── ASTBitAnd.java │ │ │ ├── ASTBitNegate.java │ │ │ ├── ASTBitOr.java │ │ │ ├── ASTChain.java │ │ │ ├── ASTConst.java │ │ │ ├── ASTCtor.java │ │ │ ├── ASTDivide.java │ │ │ ├── ASTEq.java │ │ │ ├── ASTEval.java │ │ │ ├── ASTGreater.java │ │ │ ├── ASTGreaterEq.java │ │ │ ├── ASTIn.java │ │ │ ├── ASTInstanceof.java │ │ │ ├── ASTKeyValue.java │ │ │ ├── ASTLess.java │ │ │ ├── ASTLessEq.java │ │ │ ├── ASTList.java │ │ │ ├── ASTMap.java │ │ │ ├── ASTMethod.java │ │ │ ├── ASTMultiply.java │ │ │ ├── ASTNegate.java │ │ │ ├── ASTNot.java │ │ │ ├── ASTNotEq.java │ │ │ ├── ASTNotIn.java │ │ │ ├── ASTOr.java │ │ │ ├── ASTProject.java │ │ │ ├── ASTProperty.java │ │ │ ├── ASTRemainder.java │ │ │ ├── ASTRootVarRef.java │ │ │ ├── ASTSelect.java │ │ │ ├── ASTSelectFirst.java │ │ │ ├── ASTSelectLast.java │ │ │ ├── ASTSequence.java │ │ │ ├── ASTShiftLeft.java │ │ │ ├── ASTShiftRight.java │ │ │ ├── ASTStaticField.java │ │ │ ├── ASTStaticMethod.java │ │ │ ├── ASTSubtract.java │ │ │ ├── ASTTest.java │ │ │ ├── ASTThisVarRef.java │ │ │ ├── ASTUnsignedShiftRight.java │ │ │ ├── ASTVarRef.java │ │ │ ├── ASTXor.java │ │ │ ├── AbstractMemberAccess.java │ │ │ ├── AccessibleObjectHandler.java │ │ │ ├── ArrayElementsAccessor.java │ │ │ ├── ArrayPropertyAccessor.java │ │ │ ├── BooleanExpression.java │ │ │ ├── ClassCacheInspector.java │ │ │ ├── ClassResolver.java │ │ │ ├── CollectionElementsAccessor.java │ │ │ ├── ComparisonExpression.java │ │ │ ├── DefaultClassResolver.java │ │ │ ├── DefaultTypeConverter.java │ │ │ ├── DynamicSubscript.java │ │ │ ├── ElementsAccessor.java │ │ │ ├── EnumerationElementsAccessor.java │ │ │ ├── EnumerationIterator.java │ │ │ ├── EnumerationPropertyAccessor.java │ │ │ ├── Evaluation.java │ │ │ ├── EvaluationPool.java │ │ │ ├── ExpressionNode.java │ │ │ ├── ExpressionSyntaxException.java │ │ │ ├── InappropriateExpressionException.java │ │ │ ├── IteratorElementsAccessor.java │ │ │ ├── IteratorEnumeration.java │ │ │ ├── IteratorPropertyAccessor.java │ │ │ ├── JJTOgnlParserState.java │ │ │ ├── JavaSource.java │ │ │ ├── ListPropertyAccessor.java │ │ │ ├── MapElementsAccessor.java │ │ │ ├── MapPropertyAccessor.java │ │ │ ├── MemberAccess.java │ │ │ ├── MethodAccessor.java │ │ │ ├── MethodFailedException.java │ │ │ ├── NoSuchPropertyException.java │ │ │ ├── Node.java │ │ │ ├── NodeType.java │ │ │ ├── NullHandler.java │ │ │ ├── NumberElementsAccessor.java │ │ │ ├── NumericCasts.java │ │ │ ├── NumericDefaults.java │ │ │ ├── NumericExpression.java │ │ │ ├── NumericLiterals.java │ │ │ ├── NumericTypes.java │ │ │ ├── NumericValues.java │ │ │ ├── ObjectElementsAccessor.java │ │ │ ├── ObjectIndexedPropertyDescriptor.java │ │ │ ├── ObjectMethodAccessor.java │ │ │ ├── ObjectNullHandler.java │ │ │ ├── ObjectPropertyAccessor.java │ │ │ ├── Ognl.java │ │ │ ├── OgnlCache.java │ │ │ ├── OgnlContext.java │ │ │ ├── OgnlException.java │ │ │ ├── OgnlOps.java │ │ │ ├── OgnlParserTreeConstants.java │ │ │ ├── OgnlRuntime.java │ │ │ ├── PrimitiveDefaults.java │ │ │ ├── PrimitiveTypes.java │ │ │ ├── PrimitiveWrapperClasses.java │ │ │ ├── PropertyAccessor.java │ │ │ ├── SetPropertyAccessor.java │ │ │ ├── SimpleNode.java │ │ │ ├── TypeConverter.java │ │ │ ├── enhance/ │ │ │ │ ├── ContextClassLoader.java │ │ │ │ ├── EnhancedClassLoader.java │ │ │ │ ├── ExpressionAccessor.java │ │ │ │ ├── ExpressionCompiler.java │ │ │ │ ├── LocalReference.java │ │ │ │ ├── OgnlExpressionCompiler.java │ │ │ │ ├── OgnlLocalReference.java │ │ │ │ ├── OrderedReturn.java │ │ │ │ └── UnsupportedCompilationException.java │ │ │ ├── internal/ │ │ │ │ ├── Cache.java │ │ │ │ ├── CacheException.java │ │ │ │ ├── CacheFactory.java │ │ │ │ ├── ClassCache.java │ │ │ │ ├── ClassCacheHandler.java │ │ │ │ ├── HashMapCache.java │ │ │ │ ├── HashMapCacheFactory.java │ │ │ │ ├── HashMapClassCache.java │ │ │ │ └── entry/ │ │ │ │ ├── CacheEntry.java │ │ │ │ ├── CacheEntryFactory.java │ │ │ │ ├── ClassCacheEntryFactory.java │ │ │ │ ├── DeclaredMethodCacheEntry.java │ │ │ │ ├── DeclaredMethodCacheEntryFactory.java │ │ │ │ ├── FieldCacheEntryFactory.java │ │ │ │ ├── GenericMethodParameterTypeCacheEntry.java │ │ │ │ ├── GenericMethodParameterTypeFactory.java │ │ │ │ ├── MethodAccessCacheEntryFactory.java │ │ │ │ ├── MethodAccessEntryValue.java │ │ │ │ ├── MethodCacheEntry.java │ │ │ │ ├── MethodCacheEntryFactory.java │ │ │ │ └── PropertyDescriptorCacheEntryFactory.java │ │ │ └── package.html │ │ ├── javacc/ │ │ │ └── ognl.jj │ │ └── jjtree/ │ │ └── ognl.jjt │ └── test/ │ └── java/ │ ├── ClassInDefaultPackage.java │ ├── com/ │ │ └── sun/ │ │ └── test/ │ │ └── AnotherInternalClass.java │ ├── ognl/ │ │ ├── DefaultMemberAccess.java │ │ ├── ExcludedObjectMemberAccess.java │ │ ├── ObjectPropertyAccessorTest.java │ │ ├── OgnlContextTest.java │ │ ├── OgnlRuntimeAccessibilityTest.java │ │ ├── OgnlRuntimeMethodsTest.java │ │ ├── OgnlRuntimeTest.java │ │ └── test/ │ │ ├── ASTChainTest.java │ │ ├── ASTMethodTest.java │ │ ├── ASTPropertyTest.java │ │ ├── ASTSequenceTest.java │ │ ├── ArithmeticAndLogicalOperatorsOnEnumsTest.java │ │ ├── ArithmeticAndLogicalOperatorsTest.java │ │ ├── ArrayCreationTest.java │ │ ├── ArrayElementsTest.java │ │ ├── ChainTest.java │ │ ├── ClassMethodTest.java │ │ ├── CollectionDirectPropertyTest.java │ │ ├── CompilingPropertyAccessor.java │ │ ├── ConstantTest.java │ │ ├── ConstantTreeTest.java │ │ ├── ContextRootPreservationTest.java │ │ ├── ContextVariableTest.java │ │ ├── CorrectedObjectNullHandler.java │ │ ├── DefaultClassResolverTest.java │ │ ├── DualModeEvaluationTest.java │ │ ├── GenericsTest.java │ │ ├── InExpressionTest.java │ │ ├── IndexAccessTest.java │ │ ├── IndexedPropertyTest.java │ │ ├── InheritedMethodsTest.java │ │ ├── InterfaceInheritanceTest.java │ │ ├── IsTruckTest.java │ │ ├── Issue286Test.java │ │ ├── Issue472CustomMethodAccessorTest.java │ │ ├── Java8Test.java │ │ ├── LambdaExpressionTest.java │ │ ├── MapCreationTest.java │ │ ├── MemberAccessTest.java │ │ ├── MethodTest.java │ │ ├── MethodWithConversionTest.java │ │ ├── NestedMethodTest.java │ │ ├── NullHandlerTest.java │ │ ├── NullRootTest.java │ │ ├── NullSafeCollectionTest.java │ │ ├── NullSafeCompilationTest.java │ │ ├── NullSafeIntegrationTest.java │ │ ├── NullSafeOperatorTest.java │ │ ├── NullStringCatenationTest.java │ │ ├── NumberFormatExceptionTest.java │ │ ├── NumericConversionTest.java │ │ ├── ObjectIndexedPropertyTest.java │ │ ├── ObjectIndexedTest.java │ │ ├── OgnlContextCreateTest.java │ │ ├── OgnlExceptionTest.java │ │ ├── OgnlOpsTest.java │ │ ├── OperationTest.java │ │ ├── OperatorTest.java │ │ ├── PackageKeywordTest.java │ │ ├── PrimitiveArrayTest.java │ │ ├── PrimitiveNullHandlingTest.java │ │ ├── PrivateAccessorTest.java │ │ ├── PrivateMemberTest.java │ │ ├── ProjectionSelectionTest.java │ │ ├── PropertyAccessorTest.java │ │ ├── PropertyArithmeticAndLogicalOperatorsTest.java │ │ ├── PropertySetterTest.java │ │ ├── PropertyTest.java │ │ ├── ProtectedInnerClassTest.java │ │ ├── ProtectedMemberTest.java │ │ ├── PublicMemberTest.java │ │ ├── QuotingTest.java │ │ ├── RaceConditionTest.java │ │ ├── SetterTest.java │ │ ├── SetterWithConversionTest.java │ │ ├── ShortCircuitingExpressionTest.java │ │ ├── SimpleNavigationChainTreeTest.java │ │ ├── SimplePropertyTreeTest.java │ │ ├── StaticsAndConstructorsTest.java │ │ ├── VarArgsMethodTest.java │ │ ├── accessors/ │ │ │ ├── ListPropertyAccessorTest.java │ │ │ └── PropertyAccessTest.java │ │ ├── enhance/ │ │ │ └── ExpressionCompilerTest.java │ │ ├── objects/ │ │ │ ├── BaseBean.java │ │ │ ├── BaseGeneric.java │ │ │ ├── BaseIndexed.java │ │ │ ├── BaseObjectIndexed.java │ │ │ ├── BaseSyntheticObject.java │ │ │ ├── Bean1.java │ │ │ ├── Bean2.java │ │ │ ├── Bean3.java │ │ │ ├── BeanProvider.java │ │ │ ├── BeanProviderAccessor.java │ │ │ ├── BeanProviderImpl.java │ │ │ ├── Component.java │ │ │ ├── ComponentImpl.java │ │ │ ├── ComponentSubclass.java │ │ │ ├── Copy.java │ │ │ ├── CorrectedObject.java │ │ │ ├── Cracker.java │ │ │ ├── Entry.java │ │ │ ├── EvenOdd.java │ │ │ ├── FirstBean.java │ │ │ ├── FormComponentImpl.java │ │ │ ├── FormImpl.java │ │ │ ├── GameGeneric.java │ │ │ ├── GameGenericObject.java │ │ │ ├── GenericCracker.java │ │ │ ├── GenericObject.java │ │ │ ├── GenericRoot.java │ │ │ ├── GenericService.java │ │ │ ├── GenericServiceImpl.java │ │ │ ├── GetterMethods.java │ │ │ ├── IComponent.java │ │ │ ├── IContentProvider.java │ │ │ ├── IForm.java │ │ │ ├── IFormComponent.java │ │ │ ├── ITreeContentProvider.java │ │ │ ├── Indexed.java │ │ │ ├── IndexedMapObject.java │ │ │ ├── IndexedSetObject.java │ │ │ ├── Inherited.java │ │ │ ├── ListSource.java │ │ │ ├── ListSourceImpl.java │ │ │ ├── MenuItem.java │ │ │ ├── Messages.java │ │ │ ├── MethodTestMethods.java │ │ │ ├── Model.java │ │ │ ├── MyMap.java │ │ │ ├── MyMapImpl.java │ │ │ ├── ObjectIndexed.java │ │ │ ├── OtherEnum.java │ │ │ ├── OtherObjectIndexed.java │ │ │ ├── PersonGenericObject.java │ │ │ ├── PropertyHolder.java │ │ │ ├── Root.java │ │ │ ├── SearchCriteria.java │ │ │ ├── SearchTab.java │ │ │ ├── SecondBean.java │ │ │ ├── SetterReturns.java │ │ │ ├── Simple.java │ │ │ ├── SimpleEnum.java │ │ │ ├── SimpleNumeric.java │ │ │ ├── StaticInterface.java │ │ │ ├── SubclassSyntheticObject.java │ │ │ ├── TestClass.java │ │ │ ├── TestImpl.java │ │ │ ├── TestInherited1.java │ │ │ ├── TestInherited2.java │ │ │ ├── TestModel.java │ │ │ ├── TreeContentProvider.java │ │ │ └── Two.java │ │ ├── race/ │ │ │ ├── Base.java │ │ │ ├── Person.java │ │ │ └── RaceTest.java │ │ └── util/ │ │ ├── ContextClassLoader.java │ │ ├── EnhancedClassLoader.java │ │ └── NameFactory.java │ └── sun/ │ └── test/ │ ├── PublicTestInterface.java │ └── SimulatedInternalClass.java ├── pom.xml └── renovate.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .claude/settings.json ================================================ { "permissions": { "allow": [ "WebSearch", "WebFetch(domain:github.com)", "WebFetch(domain:raw.githubusercontent.com)", "Bash(mvn help:*)", "Bash(mvn test:*)", "Bash(git branch:*)", "Bash(git add:*)", "Bash(git commit:*)", "Bash(git push:*)", "Bash(git checkout:*)", "Bash(git reset:*)", "Bash(gh pr view:*)", "Bash(gh pr diff:*)", "Bash(gh pr create:*)", "mcp__jetbrains" ], "deny": [] } } ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [lukaszlenart] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: "maven/ognl:ognl" # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/workflows/maven.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Java CI - main on: pull_request: push: branches: - main permissions: read-all env: MAVEN_OPTS: -Xmx2g -Xms1g LANG: en_US.utf8 jobs: build: runs-on: ubuntu-latest strategy: matrix: java: [ '17', '21', '25' ] steps: - name: Checkout code uses: actions/checkout@v6.0.2 - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v5 with: distribution: temurin java-version: ${{ matrix.java }} cache: maven - name: Build with Maven on Java ${{ matrix.java }} run: ./mvnw -B package -DskipTests - name: Test with Maven on Java ${{ matrix.java }} run: ./mvnw -B verify - name: Check if SNAPSHOT version id: is-snapshot if: matrix.java == '17' && github.ref == 'refs/heads/main' run: | VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout) if [[ "$VERSION" == *-SNAPSHOT ]]; then echo "is_snapshot=true" >> $GITHUB_OUTPUT else echo "is_snapshot=false" >> $GITHUB_OUTPUT fi - name: Deploy SNAPSHOT if: matrix.java == '17' && github.ref == 'refs/heads/main' && steps.is-snapshot.outputs.is_snapshot == 'true' run: | echo "${{ secrets.MAVEN_SETTINGS }}" > ~/.m2/settings-ognl.xml ./mvnw -B -DskipTests=true deploy -s ~/.m2/settings-ognl.xml ================================================ FILE: .github/workflows/ognl-3-4-x.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Java CI - ognl-3-4-x on: push: branches: - ognl-3-4-x permissions: read-all env: MAVEN_OPTS: -Xmx2g -Xms1g LANG: en_US.utf8 jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6.0.2 - name: Set up JDK 8 uses: actions/setup-java@v5 with: distribution: temurin java-version: 8 cache: maven - name: Build with Maven on Java 9 run: ./mvnw -B package -DskipTests - name: Test with Maven on Java 8 run: ./mvnw -B verify - name: Deploy SNAPSHOT run: | echo "${{ secrets.MAVEN_SETTINGS }}" > ~/.m2/settings-ognl.xml ./mvnw -B -DskipTests=true deploy -s ~/.m2/settings-ognl.xml ================================================ FILE: .github/workflows/performance-baseline.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: JMH Baseline Generation on: workflow_dispatch: permissions: contents: read env: MAVEN_OPTS: -Xmx2g -Xms1g LANG: en_US.utf8 jobs: generate-baseline: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6.0.2 - name: Set up cache uses: actions/cache@v5.0.5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK 17 uses: actions/setup-java@v5 with: distribution: temurin java-version: 17 - name: Build with Maven run: ./mvnw -B package -DskipTests - name: Run benchmarks (10 forks for baseline) env: OPTIONS: generateBaseline run: ./mvnw -B verify -Pbenchmarks -DskipTests -Dbenchmarks.jmhArgs="-f 10" - name: Upload baseline JSON uses: actions/upload-artifact@v7 with: name: jmh-baseline path: benchmarks/target/ognl-runtime-benchmark-results.json ================================================ FILE: .github/workflows/performance.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: JMH Benchmarks on: workflow_dispatch: push: branches: - main permissions: read-all env: MAVEN_OPTS: -Xmx2g -Xms1g LANG: en_US.utf8 jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6.0.2 - name: Set up cache uses: actions/cache@v5.0.5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK 17 uses: actions/setup-java@v5 with: distribution: temurin java-version: 17 - name: Build with Maven run: ./mvnw -B package -DskipTests - name: Run benchmarks with Maven env: OPTIONS: publishSummary run: | echo '### Benchmarks summary 🚀' >> $GITHUB_STEP_SUMMARY ./mvnw -B verify -Pbenchmarks -DskipTests -Dbenchmarks.jmhArgs="-f 10" - name: Upload JMH results uses: actions/upload-artifact@v7 with: name: jmh-results path: benchmarks/target/ognl-runtime-benchmark-results.json ================================================ FILE: .github/workflows/sonar.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: SonarCloud on: pull_request: push: branches: - 'main' permissions: read-all env: MAVEN_OPTS: -Xmx2g -Xms1g LANG: en_US.utf8 jobs: sonarcloud: name: Scan runs-on: ubuntu-latest if: ${{ !github.event.pull_request.base.repo.fork && !github.event.pull_request.head.repo.fork && github.actor != 'renovate[bot]' }} steps: - name: Checkout code uses: actions/checkout@v6 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Setup Java uses: actions/setup-java@v5 with: distribution: temurin java-version: 17 cache: 'maven' - name: Cache SonarQube packages uses: actions/cache@v5 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Build with Maven run: ./mvnw -B package -DskipTests=true - name: Test with coverage run: ./mvnw -B verify -Pcoverage - name: SonarCloud Scan on ${{ github.ref }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: ./mvnw -B -Pcoverage sonar:sonar ================================================ FILE: .gitignore ================================================ *.iml *.ipr *.iws target .idea build .java-version .mvn/wrapper/maven-wrapper.jar .DS_Store **/.DS_Store .claude/settings.local.json .worktrees ================================================ FILE: .mvn/wrapper/MavenWrapperDownloader.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 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 java.net.*; import java.io.*; import java.nio.channels.*; import java.util.Properties; public class MavenWrapperDownloader { /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar"; /** * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to * use instead of the default one. */ private static final String MAVEN_WRAPPER_PROPERTIES_PATH = ".mvn/wrapper/maven-wrapper.properties"; /** * Path where the maven-wrapper.jar will be saved to. */ private static final String MAVEN_WRAPPER_JAR_PATH = ".mvn/wrapper/maven-wrapper.jar"; /** * Name of the property which should be used to override the default download url for the wrapper. */ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; public static void main(String args[]) { System.out.println("- Downloader started"); File baseDirectory = new File(args[0]); System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); // If the maven-wrapper.properties exists, read it and check if it contains a custom // wrapperUrl parameter. File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); String url = DEFAULT_DOWNLOAD_URL; if(mavenWrapperPropertyFile.exists()) { FileInputStream mavenWrapperPropertyFileInputStream = null; try { mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); Properties mavenWrapperProperties = new Properties(); mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); } catch (IOException e) { System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); } finally { try { if(mavenWrapperPropertyFileInputStream != null) { mavenWrapperPropertyFileInputStream.close(); } } catch (IOException e) { // Ignore ... } } } System.out.println("- Downloading from: : " + url); File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); if(!outputFile.getParentFile().exists()) { if(!outputFile.getParentFile().mkdirs()) { System.out.println( "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); } } System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); try { downloadFileFromURL(url, outputFile); System.out.println("Done"); System.exit(0); } catch (Throwable e) { System.out.println("- Error downloading"); e.printStackTrace(); System.exit(1); } } private static void downloadFileFromURL(String urlString, File destination) throws Exception { URL website = new URL(urlString); ReadableByteChannel rbc; rbc = Channels.newChannel(website.openStream()); FileOutputStream fos = new FileOutputStream(destination); fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); fos.close(); rbc.close(); } } ================================================ FILE: .mvn/wrapper/maven-wrapper.properties ================================================ distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip ================================================ FILE: CLAUDE.md ================================================ # CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview OGNL (Object-Graph Navigation Language) is an expression language for getting and setting properties of Java objects, used by frameworks including Apache Struts. Key features: property navigation (JavaBeans), method invocation, collection operations (projection/selection), lambda expressions, type conversion, and member access control. ## Development Environment - **Language**: Java 17 (CI also tests against Java 21 and 25) - **Build Tool**: Maven with wrapper (`./mvnw`), multi-module project - **Testing**: JUnit Jupiter 6.x - **Parser Generator**: JavaCC (grammar at `ognl/src/main/javacc/ognl.jj`) ## Project Structure - **ognl/** — Core library (source: `ognl/src/main/java/ognl/`, tests: `ognl/src/test/java/ognl/`) - **benchmarks/** — JMH performance benchmarks - **docs/** — Language Guide, Developer Guide, Version Notes ## Essential Commands ```bash # Build ./mvnw clean install # Full build + install ./mvnw compile # Compile only (includes JavaCC parser generation) # Tests ./mvnw test # Full test suite ./mvnw test -pl ognl # Core module tests only ./mvnw test -pl ognl -Dtest=ClassName # Single test class ./mvnw test -pl ognl -Dtest=Pattern -Dsurefire.failIfNoSpecifiedTests=false # Pattern match # Coverage & Quality ./mvnw clean test -Pcoverage # JaCoCo coverage report ./mvnw sonar:sonar -Pcoverage # SonarCloud analysis # Benchmarks cd benchmarks && ../mvnw clean install && java -jar target/benchmarks.jar ``` ## JavaCC Parser Generation - Parser auto-generated from `ognl/src/main/javacc/ognl.jj` during `compile` phase - Generated sources go to `ognl/target/generated-sources/java/` - To regenerate AST files, uncomment `*.jtree` in `ognl/pom.xml` and change goal to `jtree-javacc` ## Architecture ### Evaluation Flow 1. Expression string → parsed into AST tree via JavaCC (`OgnlParser`) 2. AST evaluated against a root object within an `OgnlContext` 3. Each AST node type handles its own evaluation via `SimpleNode.getValue()`/`setValue()` 4. `OgnlRuntime` resolves properties, methods, and fields via reflection (with caching) 5. Results pass through `TypeConverter` when type coercion is needed ## SonarCloud - **Project**: `orphan-oss_ognl` — https://sonarcloud.io/project/overview?id=orphan-oss_ognl - **Quality Gate**: Must pass for all PRs - **PR issues URL**: `https://sonarcloud.io/project/issues?issueStatuses=OPEN%2CCONFIRMED&sinceLeakPeriod=true&pullRequest=[PR_NUMBER]&id=orphan-oss_ognl` - Focus on new issues only, not pre-existing ones - Aim for >80% coverage on new code ## Critical Development Rules ### Context Root Preservation **Always preserve the original context root during nested evaluations.** The `addDefaultContext()` method in `Ognl.java` can overwrite original root contexts during list processing. Preserve original root when: - Initial context exists with non-null root - Context contains user variables (`size() > 0`) - New root differs from existing root (indicates nested evaluation) - `#root` must always refer to original context root - `#this` changes scope during collection iteration - Preserve user context variables during projection/selection (`ASTProject`/`ASTSelect`) ### Constraints - Public methods in `Ognl` class are stable API — maintain backward compatibility - Respect `MemberAccess` restrictions for private/protected access - Honor expression length limits (`expressionMaxLength`) - Use stricter invocation mode to prevent dangerous method calls ================================================ FILE: KEYS ================================================ This file contains the PGP keys of various Apache developers. Please don't use them for email unless you have to. Their main purpose is code signing. Apache users: pgp < KEYS Apache developers: (pgpk -ll && pgpk -xa ) >> this file. or (gpg --fingerprint --list-sigs && gpg --armor --export ) >> this file. Apache developers: please ensure that your key is also available via the PGP keyservers (such as pgpkeys.mit.edu). pub 1024D/DD8B8819 2009-12-16 uid Lukasz Lenart sig 3 DD8B8819 2009-12-16 Lukasz Lenart sub 4096g/66EFAD45 2009-12-16 sig DD8B8819 2009-12-16 Lukasz Lenart -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.9 (Cygwin) mQGiBEsouosRBADqYHGJ4BAM+v9OqrT0gzuZrnIxpimLNsZkj6WxO5r/Ub/kVBB4 GOZk65Bq26M+S1oMZo4jaI3+il8XZquUUa87gfBDcoVgiw32QUBvZzT3ietckI4o IGJu+oHggxQUdiyoAfz+3gvCGUc6kVYuSuFWgpwOwD9giPUIPV+eHnRLcwCgwHks wNaMFpvRzBrGTn4s+NhL+VMEALxZPmMLyIBQZ3zFcdsNmumkfv6HZ6DKJl3EILQA Eje8ihhKrpSdXXiQSNSNoXRwr4iEXJtdzkynLzHckmJMxp/T20sjZ+giPFsZP3El PSME1tfWr2X+K/DeoNAPDgZ1XwT/MbeMQTB9mttxDLh7iT0ydr1jZmc0G9072RX5 LLGmBADFRhTIif0kywiJyjLM47+H/ZMaPU8+hMuszfvSCL7HE9EXQmq743rNAlNh e/JC+Hpx/w6424nFMVQ6PQoUzWG2W8O9GuD6UTjXdhn7Gz7kH3pXvWnRWlMroGvP RMAtOUX3UL3VfCmVlag3nGqw9a4ZP4Cst1RTOY1NDKQ3NfXN17QnTHVrYXN6IExl bmFydCA8bHVrYXN6bGVuYXJ0QGFwYWNoZS5vcmc+iGAEExECACAFAksouosCGwMG CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRD1WHMi3YuIGTZ/AKCruOnMYkXXOjg6 CccBJFVbg4OBKACdH5CFT0zUSlrF1SM+S2etNT8A3iG5BA0ESyi6ixAQAIfLz7XS wcC5L32glQ/71InGR9wi1KVchAvP+soDuicqzS8kmvuxsYYyrSOTjrLRGs1kuZ3c uSTj2jgYD4peDVYyfhDDDe9PBsv3cN35XE7i/DII1qgfe5+cjND/6Wgwjcg5Doat zLrFcHCexKZKP21lG4AwblzvNojOuCvhnAPS+zbsqjQ1W0hByUH5TMaNTk0ZctXM WJzxYT5JBeboR+T/K2d0yYGUsq/A0DOtZ0JVzfDihF8QTzcbagFyP4y1O3Bp4TXd NSoETirrVnc+CyVAGMfNUpodNJizqytZlPMCujY7VhiOBvzKlc2Bq9E4TtE2g5Sd rKo8I/pYFNSUa/hkMcxj66VmC0LwNbbPTwFiUwloMWBYMH/nbq8oVvKU0NHnMyM6 FrwroPaFw5mCF7wesbJOwtW3vj5r8BCyfrpWbQXMlix14FOLaC10NG9NBGb9b648 ADEN4hNIUwBkhAiHR+Q374CRINqoe67jRgpyaW+CfdRhLM/m7Nuhj61VrUnZRQ1l B5taCw83GVsT+tTO5KiN6uLwJhkewgR3QXeuwOuhc1NY1EXy0rhH6eAMkwT03jPw AoMRLyLr97nBcHAB2XF1s5xCL16dmAnl5fpIgCtNQHZDp67un6/+0Yhbgr2bFrw6 ALq51cS79dhKIkressxI8439Odfg1B2qD1uHAAMFD/9L1n9HrSLlGtlHT8Cv6SV3 Z7o4piDM7jCUWfduNtYv8otwEq9sXYr6pAvjfTdq9jeyQrenpsuLni2eMTAJXhqz NfhSXfGRxdgC64TtK9LR9xIoZJQ0UQO5j4GFatbzvnLVJCf0TFZ9dEtrqEPMxjrc wg28Wy/UMlsd2W/FENll3bhAB4IUnf0h4NFZ42a2BKw91DIDGTap4r2gAWLMHR1y iyfGAZvmiMPocCE7Rgp2tNiUMVr01ZEcRmzZW5wg+2PpmkAR7BnVMwjsO6Bu+8fi C4q1pLLz1lRgru0lsZL/TxcXKRp94lElJ1V6Gz5MzCdY/iTnXgEXhXLTi0J2eUqQ xcYTiuI33uFRo1VsgHugkvFTlPe02fN+2tDJpG8mT0TQJT3+NdXfcK7Fg58DnP2W q7LLjJYHMZyPInXyfeVuT7a01yFkWmjKfl8gqrch1mlMaf2gkgGkMIT5gYEeA73U sfZtzcq0H0ciQyM4N0oXEbjo5/pwEQDeyZCpTI2z3cXq1N7zHhjv/gRWw51LZ65W bPJfQZwL7EnBidJFIYLMtf3wD4xheI5wgfhc7CfEoSoAgLiwPziIPS2ABM5TuYBd zuDvKSqd1VOIr0W+tHliXABRZEmQ7sONU9VptTmOIAIgB0uR4nBbt3OcEbNm2YIu V2krcFt8xJiczsflYfdF4IhJBBgRAgAJBQJLKLqLAhsMAAoJEPVYcyLdi4gZJdIA nA+r83IgYrCk9E5s2T3JhAEpPXX5AJ0d82Ug+2JyJvNyL8O+/61GrdDgSw== =+tUp -----END PGP PUBLIC KEY BLOCK----- pub 4096R/63AFBB1F 2010-05-23 Key fingerprint = 0E00 8698 344E 62B9 0633 B7C6 2841 6106 63AF BB1F uid Lukasz Lenart (Signing Apache Distributions) sig 3 63AFBB1F 2010-05-23 Lukasz Lenart (Signing Apache Distributions) -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.9 (Cygwin) mQINBEv43AABEAC6VkF/h0OnvcppCPkqzfNwNy/+D+aFhc+DESwxEznSSKSbeg3V r1wwTy90+7S/mEItm88RmkNBYe1Mz3syDcj9s4S34atEV6XzGAa+3gp2rkWskyZ9 jJhXuD5ctd3LWRsA+0b14oo4/Je4pfu5aVzEDscrorskueHOY1Z73OmwRGZlIBT9 bQU+wAkIwhkw7HepgSyLcwblcy+cM4P68Ir/HAEt19FDKtnUUTnqnKT8bQDaXUuH 2iqNYTo3xZ6D2Eh6aBcUXgzAXHtuSnm4IEwipvi1OGo87N3ZEy++bs9GLNoI8ooC pwhYtYDetOTvujXkyi3SGRzVhKagcJQhZGcRq3587f49K0EJCumyNYw5q4E1FgKN T0k3Zed/LyaUSQZBGIWcmbtXaY+2s8wtUjBXFTYbbLlbijaD95RvL8xY9mWDPwJB Pe9DidEQ9qOSNC7jWnbwKhoeTN2AAQCcaZ5lNEx3SYw0pLz7H22l+/i2jBMzYqAu 0ViRyiXkNstxrdmbTaR4hJdf3IH0fsJPjdJVV0dyHKcDKaUj1RmcUizBLYTXY6oY nEk5kp0A2pllRwE4ZLxeiT9KkqbPytqkQVGDzu4PQnMxIv0Uu7NrIbpP6fMJlt94 3N57N3lj8FCfGU1TediS2aXZx2rLVEPWFmZWeOATwqihA1fe0leMBWPnfwARAQAB tEZMdWthc3ogTGVuYXJ0IChTaWduaW5nIEFwYWNoZSBEaXN0cmlidXRpb25zKSA8 bHVrYXN6bGVuYXJ0QGFwYWNoZS5vcmc+iQI4BBMBAgAiBQJL+NwAAhsDBgsJCAcD AgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAoQWEGY6+7H8sMD/92MJ2srnFeR7h5ULWr FKU8ynBgL4ghfmEF6/2InxUtZG/6fESKYnw1nuaFCiob8Q6KpszkRwPPjHRLr6q5 DiXdptFEBGgni2Cq4HF9722hpSukYESb9wu0CiNd+aksCEIh3VlAZJ8Yve3SYndp fvmUx49mztApqq3EUzPI+O2huMDTaFNpMOrPpWZnosIdUDAFMTq3pB5GFJSxsvlA NsWCcyWnSCuTMlbpvIgJ9ojPJfm+0IXKD2dzQdR9PtDQ1tfv60OC486FncYGuwHD qbdnY4S5hPyE9YYCc7AY3Q5J/nvBIifW8fFWBLMx1ykKQlZOc4iTBOe7o3MSTeCp X09WE/GqKwEHx2IQmWqgboMW1qJlqWBwEMPcH3bj1WnQ4zkHuOLcS9xdL+opq9xG LffLm3fynicg7Fse5qXFSh7GbLSJmc5BTqA10HCE1Ur6mcL0+W/+h2kPd+U7/OU2 wr59iQuroEouV71h9SVpSElLhTOw5rRK+AU+cBIkzvfv9dBcVNlzmsAnrJ1+Gqex 4gmPmAr7MrZpOTzs5d2dYpu1LO8w6a/2CZn69sE5AGtda0ErFMXacLpzWoIMLPYN 7Vn5LS4JLE7GSM4WDfyG93OA2spR9ofBlsSqHSlOrSTM8JaEFCPaGD/AMIWKj6ya NrjTmQt9KnGO87EwAlPDwf0hjw== =x2yb -----END PGP PUBLIC KEY BLOCK----- ================================================ FILE: LICENSE.txt ================================================ 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. ================================================ FILE: README.md ================================================

OGNL

# Object-Graph Navigation Language - OGNL [![Java CI](https://github.com/orphan-oss/ognl/actions/workflows/maven.yml/badge.svg)](https://github.com/orphan-oss/ognl/actions/workflows/maven.yml) [![Maven Central](https://maven-badges.sml.io/sonatype-central/ognl/ognl/badge.svg)](https://maven-badges.sml.io/sonatype-central/ognl/ognl/) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=orphan-oss_ognl&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=orphan-oss_ognl) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=orphan-oss_ognl&metric=coverage)](https://sonarcloud.io/summary/new_code?id=orphan-oss_ognl) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6490/badge)](https://bestpractices.coreinfrastructure.org/projects/6490) [![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) ## Note I recently renamed the `master` branch into the `main` branch. If you previously cloned this repository please perform locally update of your clone using the procedure described in [Updating a local clone after a branch name changes](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-branches-in-your-repository/renaming-a-branch#updating-a-local-clone-after-a-branch-name-changes). ## Description OGNL stands for Object-Graph Navigation Language; it is an expression language for getting and setting properties of Java objects. You use the same expression for both getting and setting the value of a property. The `ognl.Ognl` class contains convenience methods for evaluating OGNL expressions. You can do this in two stages, parsing an expression into an internal form and then using that internal form to either set or get the value of a property; or you can do it in a single stage, and get or set a property using the String form of the expression directly. OGNL started out as a way to set up associations between UI components and controllers using property names. As the desire for more complicated associations grew, Drew Davidson created what he called KVCL, for Key-Value Coding Language, egged on by Luke Blanshard. Luke then reimplemented the language using ANTLR, came up with the new name, and, egged on by Drew, filled it out to its current state. Later on Luke again reimplemented the language using JavaCC. Further maintenance on all the code is done by Drew (with spiritual guidance from Luke). We pronounce OGNL as a word, like the last syllables of a drunken pronunciation of "orthogonal." - [Language Guide](docs/LanguageGuide.md) - [Developer Guide](docs/DeveloperGuide.md) - [Version Notes](docs/VersionNotes.md) ## Apache Commons OGNL project Sometimes ago this project has been migrated to [Apache Commons](http://commons.apache.org/ognl/) with a plan to maintain it there. Right now that project is considered dead and not actively maintained. There is no plans to release a new version under Apache Software Foundation umbrella and all the future development will happen here. ## Commercial Support The project maintaners are working with Tidelift to provide commercial support and invest paid working time in the improvement of the projects. For more information, visit the [Tidelift resources](https://tidelift.com/subscription/pkg/maven-ognl.ognl?utm_source=maven-ognl.ognl&utm_medium=referral&utm_campaign=enterprise) regarding OGNL. ## Contributing If you would like to start contributing to this project please follow the following guidelines: - [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) - [First-contributions project](https://github.com/firstcontributions/first-contributions) Even the smallest contribution makes sense, like improving code formatting, fixing a typo documentation. Do not hesitate to ask or report an issue. ## FAQ - How to define an AccessMember? - the best way is to implement your own `AccessMember` which will suite your project best, you can base on existing [DefaultAccessMember](src/test/java/ognl/DefaultMemberAccess.java) and adjust it to your needs. Since version 3.2.16 there is `AbstractAccessMemeber` which can be used a start point for your own implementation, see the example below: ``` MemberAccess memberAccess = new AbstractMemberAccess() { @Override public boolean isAccessible(Map context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); return Modifier.isPublic(modifiers); } }; ``` - How to use the latest SNAPSHOT version? - Define OSS Sonatype repository in `~/.m2/settings.xml` as follows: ```xml ... local true oss-snapshots https://oss.sonatype.org/content/repositories/snapshots/ default true ``` and now you can use SNAPSHOT version of OGNL in your project, ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions Use only the supported versions | Version | Supported | | ------- | ------------------ | | 3.4.x | :white_check_mark: | | 3.3.x | :x: | | 3.2.x | :x: | | 3.1.x | :x: | | 3.0.x | :x: | ## Reporting a Vulnerability Do not report security vulnerabilities using Github Issues, please contact [security@orphan.software](mailto:security@orphan.software) or contact [Tidelift Security](https://tidelift.com/security) directly before taking any action . ================================================ FILE: benchmarks/etc/ognl-runtime-benchmark-baseline.json ================================================ [ { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.constantExpressionCompiled", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.21785886246522956, "scoreError" : 0.03018147943769192, "scoreConfidence" : [ 0.18767738302753764, 0.24804034190292149 ], "scorePercentiles" : { "0.0" : 0.036782071796372745, "50.0" : 0.23805617224981002, "90.0" : 0.23896965025431835, "95.0" : 0.23952315914459757, "99.0" : 0.2395834304932379, "99.9" : 0.2395834304932379, "99.99" : 0.2395834304932379, "99.999" : 0.2395834304932379, "99.9999" : 0.2395834304932379, "100.0" : 0.2395834304932379 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.23865140397096707, 0.23897549057996254, 0.23891708732352057, 0.23835884902457555, 0.23871512562573236 ], [ 0.03682544596894528, 0.03681009960056216, 0.03684371899928783, 0.036782071796372745, 0.036792286729139874 ], [ 0.23713514089384732, 0.23715265923602807, 0.23741178325987036, 0.23732573703394405, 0.23725367075949994 ], [ 0.237548966233403, 0.23799373563088524, 0.23838859950542324, 0.2377209662004787, 0.23757572085370857 ], [ 0.23831041063628455, 0.2381337878440946, 0.23809261717626376, 0.23803612295821794, 0.2382727408139246 ], [ 0.23602626808481722, 0.23612378731857708, 0.2361808048362649, 0.23587185807697023, 0.23615284886876747 ], [ 0.23934618597951363, 0.2395497789502868, 0.2395013793035791, 0.2395834304932379, 0.23857728726354155 ], [ 0.2384609392844475, 0.23808289348453024, 0.23807080260281063, 0.23779660981327377, 0.23830022363028977 ], [ 0.2380848157487997, 0.2379425997661245, 0.23804154189680943, 0.23747309300889918, 0.23787742264464817 ], [ 0.23854952707547666, 0.23826206183994297, 0.23839621545360246, 0.23826062453929023, 0.23840588464203558 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.constantExpressionInterpreted", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.04272498866889172, "scoreError" : 4.0815026833279185E-5, "scoreConfidence" : [ 0.04268417364205844, 0.042765803695724997 ], "scorePercentiles" : { "0.0" : 0.04248629433945735, "50.0" : 0.04273522111097851, "90.0" : 0.042831265561411064, "95.0" : 0.042847592369982264, "99.0" : 0.042881426492322475, "99.9" : 0.042881426492322475, "99.99" : 0.042881426492322475, "99.999" : 0.042881426492322475, "99.9999" : 0.042881426492322475, "100.0" : 0.042881426492322475 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.042833341356313816, 0.04280494186398773, 0.042711629444553666, 0.0427866491204488, 0.042688583416898976 ], [ 0.042631004855829706, 0.04278364177985903, 0.04275747989293857, 0.04275549757969144, 0.04273348261886908 ], [ 0.04271014916383902, 0.042675562261277104, 0.042505679333536876, 0.04270625670875453, 0.042680210127576265 ], [ 0.042694612288823104, 0.042777636932445816, 0.04284829960761433, 0.04277579766839454, 0.042881426492322475 ], [ 0.04275697705908184, 0.04267343047606158, 0.04273485454039626, 0.0426229671793379, 0.04273011153027409 ], [ 0.042715771626300136, 0.04264915236190098, 0.042783171166586004, 0.042761808777012224, 0.042752811277094314 ], [ 0.04262411492378655, 0.04281258340728631, 0.042803977098053274, 0.04278243670992074, 0.04284160803870031 ], [ 0.04276583000063217, 0.042847013721010574, 0.042735587681560765, 0.04277096991663798, 0.04248629433945735 ], [ 0.04262397235869834, 0.04275372021244837, 0.04258856414654997, 0.042776966060787534, 0.0427362031947739 ], [ 0.04272952591924901, 0.042717467012950705, 0.042653324532282114, 0.04266244361239088, 0.042613892049388764 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationAndComparisonExpressionCompiled", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.19687898420026478, "scoreError" : 4.299416347849553E-4, "scoreConfidence" : [ 0.19644904256547982, 0.19730892583504975 ], "scorePercentiles" : { "0.0" : 0.19362440890937926, "50.0" : 0.19705926590410616, "90.0" : 0.19767431662739016, "95.0" : 0.19774768667965956, "99.0" : 0.19777187966959967, "99.9" : 0.19777187966959967, "99.99" : 0.19777187966959967, "99.999" : 0.19777187966959967, "99.9999" : 0.19777187966959967, "100.0" : 0.19777187966959967 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.19362440890937926, 0.19424291592121987, 0.19469741408249816, 0.19671245392881795, 0.19723145997928762 ], [ 0.19714620817233064, 0.19705434337718414, 0.1965862541418985, 0.1968387365586376, 0.1969860706126541 ], [ 0.1968793900420804, 0.19705477111671457, 0.19710329597605045, 0.19694801286649838, 0.1964221380936104 ], [ 0.19743481413708933, 0.19704042024398827, 0.1968827107263661, 0.196958265133343, 0.19706376069149775 ], [ 0.1970525215095089, 0.19712168545287595, 0.19553275975251633, 0.19626740020019767, 0.1970466514750965 ], [ 0.1950941077828834, 0.19740693448299815, 0.19729695539619865, 0.1973499752207165, 0.1973149346538485 ], [ 0.19730919187874418, 0.19720416362467466, 0.19735330397223502, 0.19658247392326308, 0.19732525080865007 ], [ 0.19767803922023344, 0.19738116855149765, 0.19771298281430622, 0.19775219377991446, 0.19777187966959967 ], [ 0.19705368236808787, 0.19774399905217827, 0.19729545194862297, 0.1974867601466585, 0.19582384425099467 ], [ 0.19764081329180058, 0.197347687948318, 0.197040577518173, 0.19690692795115017, 0.19714704665615046 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationAndComparisonExpressionInterpreted", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.0014216685535989933, "scoreError" : 1.4671532821293609E-5, "scoreConfidence" : [ 0.0014069970207776998, 0.0014363400864202869 ], "scorePercentiles" : { "0.0" : 0.0013330312173652251, "50.0" : 0.0014288971082600163, "90.0" : 0.0014524967534770073, "95.0" : 0.0014533271515117154, "99.0" : 0.0014579522905492329, "99.9" : 0.0014579522905492329, "99.99" : 0.0014579522905492329, "99.999" : 0.0014579522905492329, "99.9999" : 0.0014579522905492329, "100.0" : 0.0014579522905492329 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.001355758741165945, 0.0013649314540947186, 0.0013651429848169244, 0.0013595173835770948, 0.0013330312173652251 ], [ 0.0014358120557608268, 0.0013803820868413114, 0.00136794932611049, 0.0014363881006857612, 0.0014384961313341175 ], [ 0.0014491383769381857, 0.0014469578876113113, 0.0014530794186977621, 0.0014579522905492329, 0.001453629936062103 ], [ 0.0014215481238867831, 0.0014168844463751944, 0.0014440216675016989, 0.001445931884835103, 0.0014261867862730346 ], [ 0.001428242184224957, 0.001395439040548544, 0.0014355883739968693, 0.0014424708741925137, 0.0014456820244281349 ], [ 0.0014240407483746631, 0.0013981851598547789, 0.0014391355823059077, 0.0014212087867605677, 0.0014357712159220554 ], [ 0.0014525213458418933, 0.0014522754221930325, 0.0014529401102833725, 0.0014492584871263197, 0.0014422897501743487 ], [ 0.0014014544273380954, 0.0014309500125620292, 0.0014351758328241498, 0.0014281395776475398, 0.0014295520322950756 ], [ 0.0014169438457974446, 0.0014275888377615438, 0.0014310907584868754, 0.0014194596792938775, 0.00144143549982489 ], [ 0.001393527100088176, 0.0014195517085851407, 0.0014141808357292828, 0.0014079515446611837, 0.0014186365803435983 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationExpressionCompiled", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.18256560401030889, "scoreError" : 5.853082858054677E-4, "scoreConfidence" : [ 0.18198029572450342, 0.18315091229611435 ], "scorePercentiles" : { "0.0" : 0.17519323353893249, "50.0" : 0.18281450096680396, "90.0" : 0.18319365473834348, "95.0" : 0.18333844000538088, "99.0" : 0.18361191353526676, "99.9" : 0.18361191353526676, "99.99" : 0.18361191353526676, "99.999" : 0.18361191353526676, "99.9999" : 0.18361191353526676, "100.0" : 0.18361191353526676 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.1828670156570737, 0.1827622142425891, 0.18276985765430112, 0.18275187587298916, 0.18294575610152522 ], [ 0.18147756200099968, 0.1813156988477983, 0.18173941142480146, 0.18129559209248544, 0.18183847847739407 ], [ 0.18298415463601134, 0.18299043389097852, 0.1826428544123203, 0.18267299835347017, 0.18241414365915293 ], [ 0.1831182167918252, 0.17519323353893249, 0.18284648320769806, 0.1830975771972494, 0.18277847525040164 ], [ 0.1822558536484412, 0.18292210792845412, 0.18289366855085543, 0.18284702965998184, 0.1819663466921657 ], [ 0.18278181586228023, 0.18278251872590986, 0.18254633590678818, 0.18301857288726964, 0.18277489313191425 ], [ 0.1831906585920126, 0.18293485182383873, 0.18319311124434948, 0.18276838294941586, 0.18361191353526676 ], [ 0.18311589539448916, 0.18332368998886564, 0.18335646780334397, 0.1831323006714751, 0.18319371512656504 ], [ 0.18307737789255388, 0.18311942142949805, 0.18298454232971031, 0.18322889943919163, 0.1830480837676349 ], [ 0.18232478204708372, 0.18221520617434434, 0.1824179685923412, 0.18251659582162105, 0.1822351595877831 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationExpressionInterpreted", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.0014589074582631153, "scoreError" : 1.401117879932019E-5, "scoreConfidence" : [ 0.0014448962794637952, 0.0014729186370624355 ], "scorePercentiles" : { "0.0" : 0.0013150937049245305, "50.0" : 0.0014654509814346353, "90.0" : 0.0014797222294411548, "95.0" : 0.0014830328607784058, "99.0" : 0.0014852103085103423, "99.9" : 0.0014852103085103423, "99.99" : 0.0014852103085103423, "99.999" : 0.0014852103085103423, "99.9999" : 0.0014852103085103423, "100.0" : 0.0014852103085103423 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.0014368984855318526, 0.0014183254011457696, 0.0014676153104276393, 0.001466197541071527, 0.0014658613005413931 ], [ 0.0014697483726035523, 0.0014606920772280614, 0.001464098377568244, 0.001416026160437938, 0.0014447334551263213 ], [ 0.0014852103085103423, 0.0014678942047319167, 0.0014833110702798327, 0.0014735075945547398, 0.0013150937049245305 ], [ 0.0014705061339435688, 0.0014559555046992976, 0.0014608589273809546, 0.001468312850423742, 0.001468467501964901 ], [ 0.0014754347496102936, 0.001475307572814325, 0.0014756362850625565, 0.0014746798535388676, 0.00147429564340685 ], [ 0.0014665558888723696, 0.0014772593335505005, 0.0014454674872117577, 0.0014731765128266996, 0.0014568016112198336 ], [ 0.0014648284343994808, 0.001482805234822693, 0.0014736075565049194, 0.0013909399907111354, 0.001411288429811104 ], [ 0.0014564379422907904, 0.001458767067865191, 0.0014626443315743224, 0.0014567906171473436, 0.001456440695622117 ], [ 0.0014802495981411441, 0.0014797576357525337, 0.0014786381806180919, 0.0014794035726387452, 0.0014647423158348257 ], [ 0.0014648008630993802, 0.0014646217770548332, 0.0014650406623278774, 0.0014626783635891803, 0.0014369604221398664 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationWithMapExpressionCompiled", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.1230855669818411, "scoreError" : 1.627769905744729E-4, "scoreConfidence" : [ 0.12292278999126663, 0.12324834397241557 ], "scorePercentiles" : { "0.0" : 0.12244521391098585, "50.0" : 0.12301609292593685, "90.0" : 0.12362052228239559, "95.0" : 0.12376767404135422, "99.0" : 0.1238093824915757, "99.9" : 0.1238093824915757, "99.99" : 0.1238093824915757, "99.999" : 0.1238093824915757, "99.9999" : 0.1238093824915757, "100.0" : 0.1238093824915757 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.12333499853510371, 0.12341343513520953, 0.1234280565041436, 0.1228099048010937, 0.12312316118845974 ], [ 0.1238093824915757, 0.12364190736886803, 0.12375739511144779, 0.12378023717790654, 0.12369664863369528 ], [ 0.12302402877342265, 0.12337164346465745, 0.12342075063818535, 0.12321120154586687, 0.12297956196994424 ], [ 0.12268804960618454, 0.12316474410547548, 0.12272699268452819, 0.12299452849009124, 0.12260445406467538 ], [ 0.12289502469677242, 0.12244521391098585, 0.12293074079506856, 0.12300221471189939, 0.12303512252189497 ], [ 0.12268023393353647, 0.12289277665379948, 0.1231381951238994, 0.12284521773859544, 0.12278411733271091 ], [ 0.12300815707845106, 0.12321125906879414, 0.12296638737213954, 0.12318123628433598, 0.12282197491269464 ], [ 0.12299922585763041, 0.12337774274311249, 0.12332898749764402, 0.12337031852247464, 0.12340625016294382 ], [ 0.12298208709900253, 0.12264583538338027, 0.12290797410529865, 0.12257032022520939, 0.12291678701239842 ], [ 0.12309991445084548, 0.12305673586456252, 0.12320247623211752, 0.12294127160655731, 0.1226534679027651 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationWithMapExpressionInterpreted", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.0011559228794831796, "scoreError" : 7.584148289635636E-6, "scoreConfidence" : [ 0.001148338731193544, 0.0011635070277728153 ], "scorePercentiles" : { "0.0" : 0.0010971750517651381, "50.0" : 0.0011598879939759858, "90.0" : 0.0011707682494322795, "95.0" : 0.0011724082710653913, "99.0" : 0.0011789632567641357, "99.9" : 0.0011789632567641357, "99.99" : 0.0011789632567641357, "99.999" : 0.0011789632567641357, "99.9999" : 0.0011789632567641357, "100.0" : 0.0011789632567641357 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.0011505930563896123, 0.0011625007901277808, 0.0011603345442978764, 0.001157632482060026, 0.0011640238137172892 ], [ 0.0011707918933362665, 0.0011583112383725255, 0.0011789632567641357, 0.0011705554542963967, 0.0011337791179144461 ], [ 0.0011587180308913615, 0.0011398302031555296, 0.0011450442933856823, 0.0011641247093575073, 0.001166812726386446 ], [ 0.0011626143877269256, 0.0011615931016714267, 0.0011655167364492023, 0.0011599172358505983, 0.001164768640200726 ], [ 0.0011548260372791874, 0.0011581827807300154, 0.0011495469319815802, 0.001159858752101373, 0.0011581441733627875 ], [ 0.0010971750517651381, 0.0011387683555255988, 0.0011310687722074818, 0.0011373866609747801, 0.0011360724226040357 ], [ 0.0011472594506932346, 0.0011658626564049004, 0.0011633712164182588, 0.0011591400895243614, 0.0011563692299630236 ], [ 0.0011692069065443282, 0.0011505443242710782, 0.0011318619467975692, 0.0011594578092869866, 0.0011665162978994335 ], [ 0.0011717195556158735, 0.00116762442162564, 0.0011443871713886636, 0.0011711276044616583, 0.0011732500343925795 ], [ 0.0011200723950483187, 0.0011701553018363512, 0.0011659699657065158, 0.0011638499175978252, 0.0011609420277986118 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.singlePropertyExpressionCompiled", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.42685424620235685, "scoreError" : 0.001392131635588364, "scoreConfidence" : [ 0.42546211456676847, 0.4282463778379452 ], "scorePercentiles" : { "0.0" : 0.4202183601673775, "50.0" : 0.42678756952829544, "90.0" : 0.43033392791805425, "95.0" : 0.43116479540797625, "99.0" : 0.43219296682469177, "99.9" : 0.43219296682469177, "99.99" : 0.43219296682469177, "99.999" : 0.43219296682469177, "99.9999" : 0.43219296682469177, "100.0" : 0.43219296682469177 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.4202183601673775, 0.4243705826986078, 0.427798075296459, 0.4240300378630285, 0.4211305920262649 ], [ 0.43007941994206167, 0.4290113557287929, 0.42527029591600224, 0.4295974454925142, 0.4253782800200924 ], [ 0.4306473212980555, 0.42760929272638754, 0.420354439452853, 0.4261842470569711, 0.42964472467694953 ], [ 0.42246502155099275, 0.4251405656579936, 0.4233022906817506, 0.4237510530394177, 0.42634678255278297 ], [ 0.4243854362813425, 0.4250736206210216, 0.4262231051616039, 0.4258378041826735, 0.42657073381755334 ], [ 0.42724687978505166, 0.42596786102319184, 0.42773800821251823, 0.42715833000524345, 0.4263313672428556 ], [ 0.43179726376454614, 0.42971060060774535, 0.42829671902726035, 0.429406906720605, 0.42861632407670763 ], [ 0.4303364840065042, 0.43219296682469177, 0.43035951770049097, 0.42760811006964605, 0.4298921224518407 ], [ 0.42826895865396925, 0.4250707712346388, 0.4252064162792751, 0.4265006221755037, 0.4244215442557991 ], [ 0.4301530974101937, 0.42708447195293353, 0.4303109231220044, 0.425610754366033, 0.42700440523903754 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.singlePropertyExpressionInterpreted", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 0.006189197725125023, "scoreError" : 2.3556306889227494E-5, "scoreConfidence" : [ 0.006165641418235795, 0.006212754032014251 ], "scorePercentiles" : { "0.0" : 0.006099220704400432, "50.0" : 0.006176853114556585, "90.0" : 0.006259681429963715, "95.0" : 0.00626388884417367, "99.0" : 0.006265840407457353, "99.9" : 0.006265840407457353, "99.99" : 0.006265840407457353, "99.999" : 0.006265840407457353, "99.9999" : 0.006265840407457353, "100.0" : 0.006265840407457353 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 0.006224255900123771, 0.006216199295010052, 0.006225168742536318, 0.0062198920881069856, 0.006217496718379897 ], [ 0.006260784058695964, 0.006262161682381331, 0.00626446760961807, 0.006263415308810069, 0.006265840407457353 ], [ 0.006102219784422046, 0.006099220704400432, 0.006099406638767586, 0.0061111656275572085, 0.00610638073827144 ], [ 0.006148607623892926, 0.006150535819228834, 0.00616122451886336, 0.00615716796333728, 0.006157587691483904 ], [ 0.006155898800383555, 0.006146913540166463, 0.006135027189253863, 0.006147280387846762, 0.0061359715496389225 ], [ 0.006200011517849687, 0.006192567066186918, 0.006192652530058848, 0.0061735919845837045, 0.0061985507630424935 ], [ 0.00624935786871587, 0.006241457112979463, 0.006246024542408933, 0.006249757771373476, 0.00623852537175972 ], [ 0.006220591762618625, 0.006225530935171328, 0.006224134217467766, 0.006225912093466367, 0.006225867149977851 ], [ 0.00617668403363726, 0.0061699218549076335, 0.00616933047929752, 0.006170062034435757, 0.0061675864317698335 ], [ 0.006174350404251687, 0.006173122064707305, 0.0061770221954759105, 0.006167154754876996, 0.006175828926595799 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceFakeGenericMultipleThreads", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 1.218240358241585E-8, "scoreError" : 2.05521344290243E-10, "scoreConfidence" : [ 1.1976882238125607E-8, 1.2387924926706093E-8 ], "scorePercentiles" : { "0.0" : 1.1410746570944425E-8, "50.0" : 1.2229795247495642E-8, "90.0" : 1.254723462427977E-8, "95.0" : 1.317888696988531E-8, "99.0" : 1.3301447748860085E-8, "99.9" : 1.3301447748860085E-8, "99.99" : 1.3301447748860085E-8, "99.999" : 1.3301447748860085E-8, "99.9999" : 1.3301447748860085E-8, "100.0" : 1.3301447748860085E-8 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 1.255253250544193E-8, 1.2752690816290774E-8, 1.3133071823979945E-8, 1.3301447748860085E-8, 1.3234883259325205E-8 ], [ 1.1488963174439741E-8, 1.1647528118195715E-8, 1.2135304836575125E-8, 1.2265889876289669E-8, 1.244738495168379E-8 ], [ 1.1890347830604254E-8, 1.1659028459823521E-8, 1.2081905576214577E-8, 1.2172627301373773E-8, 1.2193700618701616E-8 ], [ 1.1512244648376461E-8, 1.1410746570944425E-8, 1.1853822466421107E-8, 1.2057095332471909E-8, 1.1951564884970565E-8 ], [ 1.1536106885937193E-8, 1.162023145355186E-8, 1.2380363818456452E-8, 1.232004496505568E-8, 1.2281543683210444E-8 ], [ 1.2465709924452793E-8, 1.1828433131376144E-8, 1.2361289724748185E-8, 1.2306713840926252E-8, 1.2299096691708177E-8 ], [ 1.2099520957441571E-8, 1.1812265781779913E-8, 1.2347453774141573E-8, 1.2348463878260165E-8, 1.2267081271366719E-8 ], [ 1.1890314299987363E-8, 1.1677597485036312E-8, 1.2382711374585787E-8, 1.2482275876384646E-8, 1.249955369382032E-8 ], [ 1.2054546980870276E-8, 1.1852980068247998E-8, 1.209165467172198E-8, 1.241811620742088E-8, 1.2362496233717974E-8 ], [ 1.2180685519292696E-8, 1.1967800119163984E-8, 1.2348491384190168E-8, 1.2410626164472346E-8, 1.2483228458482214E-8 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceFakeGenericSingleThread", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 6.856017979488916E-9, "scoreError" : 3.4273359127384776E-11, "scoreConfidence" : [ 6.821744620361532E-9, 6.890291338616301E-9 ], "scorePercentiles" : { "0.0" : 6.702692297225796E-9, "50.0" : 6.845058215300912E-9, "90.0" : 6.972075424714753E-9, "95.0" : 6.974805681872632E-9, "99.0" : 6.98402837914835E-9, "99.9" : 6.98402837914835E-9, "99.99" : 6.98402837914835E-9, "99.999" : 6.98402837914835E-9, "99.9999" : 6.98402837914835E-9, "100.0" : 6.98402837914835E-9 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 6.853227127919607E-9, 6.849355521242589E-9, 6.856286113540408E-9, 6.8485768227369564E-9, 6.828167425553259E-9 ], [ 6.810398286948218E-9, 6.819363948280015E-9, 6.82079252302541E-9, 6.828551381393898E-9, 6.826923241219361E-9 ], [ 6.840567381714661E-9, 6.833123187462826E-9, 6.841524627077997E-9, 6.835387224627686E-9, 6.8524607167344796E-9 ], [ 6.920910814128698E-9, 6.911654731581781E-9, 6.958294529327012E-9, 6.919472664813523E-9, 6.922952180607796E-9 ], [ 6.71087070543809E-9, 6.71335894768766E-9, 6.7132830005260045E-9, 6.718944024956293E-9, 6.702692297225796E-9 ], [ 6.974417745043749E-9, 6.98402837914835E-9, 6.973606635313391E-9, 6.974566142579633E-9, 6.975098452119632E-9 ], [ 6.847143592709135E-9, 6.835814134916784E-9, 6.83646698570329E-9, 6.841617465606336E-9, 6.842972837892689E-9 ], [ 6.848401296091644E-9, 6.8488127395752E-9, 6.8506593327526905E-9, 6.8367718532274945E-9, 6.8513548549331805E-9 ], [ 6.923532429883763E-9, 6.914979982283802E-9, 6.914294437190247E-9, 6.9185719504520185E-9, 6.9132068108443405E-9 ], [ 6.831926338607461E-9, 6.825520941845912E-9, 6.833950849132061E-9, 6.828852745334356E-9, 6.837190615488607E-9 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceMultipleClassesMultipleMethodsMultipleThreads", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 3.6933177630490017E-10, "scoreError" : 3.1643608457982276E-12, "scoreConfidence" : [ 3.6616741545910194E-10, 3.724961371506984E-10 ], "scorePercentiles" : { "0.0" : 3.5339495371824477E-10, "50.0" : 3.6994053973391465E-10, "90.0" : 3.771786467752733E-10, "95.0" : 3.784399779005753E-10, "99.0" : 3.7981014368818215E-10, "99.9" : 3.7981014368818215E-10, "99.99" : 3.7981014368818215E-10, "99.999" : 3.7981014368818215E-10, "99.9999" : 3.7981014368818215E-10, "100.0" : 3.7981014368818215E-10 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 3.647605428961105E-10, 3.662035381947934E-10, 3.636293798366089E-10, 3.672006486749277E-10, 3.671513181554741E-10 ], [ 3.5596596057703686E-10, 3.611434959318218E-10, 3.604411141071197E-10, 3.605809038554304E-10, 3.5869793915009636E-10 ], [ 3.7223889949067254E-10, 3.7950177582220396E-10, 3.6842375909190396E-10, 3.691339734891369E-10, 3.7757123414651545E-10 ], [ 3.772070282793252E-10, 3.712735604103268E-10, 3.730937755706581E-10, 3.743792532255913E-10, 3.746956601477299E-10 ], [ 3.5339495371824477E-10, 3.606268853312111E-10, 3.588093156134085E-10, 3.60125220523309E-10, 3.6341520658556556E-10 ], [ 3.669551838466956E-10, 3.7418128858099777E-10, 3.6990723048848545E-10, 3.7236881768372755E-10, 3.750092042259085E-10 ], [ 3.6958450558885984E-10, 3.6985753659490114E-10, 3.730777547361143E-10, 3.744072626178927E-10, 3.6997384897934386E-10 ], [ 3.68690147364556E-10, 3.6684320003969126E-10, 3.702353216889408E-10, 3.673236039213699E-10, 3.6615731011466364E-10 ], [ 3.775163071981809E-10, 3.7099182000469965E-10, 3.7981014368818215E-10, 3.721449069717632E-10, 3.7692321323880667E-10 ], [ 3.760420698750341E-10, 3.743650136691298E-10, 3.7521726341219615E-10, 3.748421713917879E-10, 3.7449834649786084E-10 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceMultipleClassesMultipleMethodsSingleThread", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 1.3377625184381383E-10, "scoreError" : 1.844175010420448E-12, "scoreConfidence" : [ 1.319320768333934E-10, 1.3562042685423427E-10 ], "scorePercentiles" : { "0.0" : 1.2151281988507595E-10, "50.0" : 1.3496570696796162E-10, "90.0" : 1.3547809713325093E-10, "95.0" : 1.3595675571722017E-10, "99.0" : 1.3603331676410354E-10, "99.9" : 1.3603331676410354E-10, "99.99" : 1.3603331676410354E-10, "99.999" : 1.3603331676410354E-10, "99.9999" : 1.3603331676410354E-10, "100.0" : 1.3603331676410354E-10 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 1.3495552827548611E-10, 1.3494861280976927E-10, 1.3493934813379878E-10, 1.348977190325047E-10, 1.3487568862502493E-10 ], [ 1.350560214531131E-10, 1.349267260377633E-10, 1.349312424310286E-10, 1.3476039292458625E-10, 1.3500833680124972E-10 ], [ 1.3591509413318428E-10, 1.356615184176326E-10, 1.3550545759734885E-10, 1.3600767543104181E-10, 1.3603331676410354E-10 ], [ 1.3505829964716538E-10, 1.3503096008149444E-10, 1.3501767829397524E-10, 1.350222836381941E-10, 1.349211247785485E-10 ], [ 1.232827283199682E-10, 1.235618393636287E-10, 1.2366287895240569E-10, 1.2151281988507595E-10, 1.220154867802751E-10 ], [ 1.3497588566043714E-10, 1.3509640912874515E-10, 1.3491390710244323E-10, 1.3513176452892365E-10, 1.3500684426107433E-10 ], [ 1.3523185295636954E-10, 1.3490487209790087E-10, 1.349405297930394E-10, 1.351659762664563E-10, 1.3491467653172854E-10 ], [ 1.350749480382141E-10, 1.3510011220195366E-10, 1.3493163518149941E-10, 1.3512923895340048E-10, 1.3514047322035178E-10 ], [ 1.347248651861423E-10, 1.3505173417394907E-10, 1.3499145734235457E-10, 1.3514833132868197E-10, 1.3502452375688923E-10 ], [ 1.3419388805174007E-10, 1.3401910489380967E-10, 1.3424035573558956E-10, 1.3414864997845335E-10, 1.3410177721217844E-10 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceNonGenericSingleThread", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 2.9151828493590532E-9, "scoreError" : 3.944377719486018E-11, "scoreConfidence" : [ 2.875739072164193E-9, 2.9546266265539133E-9 ], "scorePercentiles" : { "0.0" : 2.6559665024121956E-9, "50.0" : 2.9197794576134116E-9, "90.0" : 2.9938189384584366E-9, "95.0" : 3.001529949909771E-9, "99.0" : 3.0022860697117797E-9, "99.9" : 3.0022860697117797E-9, "99.99" : 3.0022860697117797E-9, "99.999" : 3.0022860697117797E-9, "99.9999" : 3.0022860697117797E-9, "100.0" : 3.0022860697117797E-9 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 2.9150938512619187E-9, 2.9127606429125684E-9, 2.915408144088862E-9, 2.914590196027409E-9, 2.920864461265813E-9 ], [ 2.9776874524301403E-9, 2.980923434416205E-9, 2.975535967498689E-9, 2.980857398798669E-9, 3.001706065418298E-9 ], [ 3.0022860697117797E-9, 2.990481169845691E-9, 2.9941898016376306E-9, 2.995188095541049E-9, 3.0013858554027947E-9 ], [ 2.7913622627190414E-9, 2.794097014432924E-9, 2.6761817726781316E-9, 2.657116051548788E-9, 2.6559665024121956E-9 ], [ 2.91869445396101E-9, 2.916191082884127E-9, 2.8523141121032163E-9, 2.9092103383463506E-9, 2.9162621624730567E-9 ], [ 2.8959288793382786E-9, 2.898995021533624E-9, 2.8932016029968646E-9, 2.8893255297556208E-9, 2.8969110617809218E-9 ], [ 2.947546995239791E-9, 2.9570415289189245E-9, 2.9572736328947623E-9, 2.958866077540453E-9, 2.974706381011721E-9 ], [ 2.941959778916469E-9, 2.9056470699700475E-9, 2.943608973809784E-9, 2.938675415800825E-9, 2.9418819742644362E-9 ], [ 2.970856136003441E-9, 2.9653255293345765E-9, 2.9583209556362793E-9, 2.95264066824762E-9, 2.9564021241288065E-9 ], [ 2.892657464675231E-9, 2.8758770074408785E-9, 2.8925382889608015E-9, 2.8907110241725276E-9, 2.895888985763669E-9 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceNotGenericMultipleThreads", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 7.237642654915351E-9, "scoreError" : 6.713711149619497E-11, "scoreConfidence" : [ 7.170505543419156E-9, 7.3047797664115454E-9 ], "scorePercentiles" : { "0.0" : 6.8494334055241105E-9, "50.0" : 7.238409852926672E-9, "90.0" : 7.397368718039454E-9, "95.0" : 7.442732323105185E-9, "99.0" : 7.539793098256803E-9, "99.9" : 7.539793098256803E-9, "99.99" : 7.539793098256803E-9, "99.999" : 7.539793098256803E-9, "99.9999" : 7.539793098256803E-9, "100.0" : 7.539793098256803E-9 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 7.309207247018319E-9, 7.204913986939781E-9, 7.301107347360866E-9, 7.227971985594883E-9, 6.955289161665703E-9 ], [ 7.384375219739969E-9, 7.539793098256803E-9, 7.1214330364272E-9, 7.397452156028071E-9, 7.278931046102976E-9 ], [ 7.299436255048755E-9, 7.152960757406128E-9, 7.37165296570325E-9, 7.24544385569135E-9, 7.208847336876574E-9 ], [ 7.073564817687222E-9, 7.109813844997201E-9, 7.224527465083748E-9, 7.196042469520692E-9, 7.139944122154705E-9 ], [ 7.2447545706131E-9, 7.164848859577679E-9, 7.246048028728349E-9, 7.2193064135468106E-9, 7.151388238211884E-9 ], [ 7.423924257204896E-9, 7.377383308518475E-9, 7.234548809215603E-9, 7.325182149604607E-9, 7.0583465512636415E-9 ], [ 7.391433889132328E-9, 7.144390195035681E-9, 7.45297338921348E-9, 7.396617776141905E-9, 7.218980935876918E-9 ], [ 7.253893902260996E-9, 7.059637268883382E-9, 7.366261971201225E-9, 7.19112433537303E-9, 6.8494334055241105E-9 ], [ 7.294428828465356E-9, 7.273391359807119E-9, 7.192613058360551E-9, 7.33132266249728E-9, 6.98912444709985E-9 ], [ 7.354252693880228E-9, 7.242270896637741E-9, 7.187680894274294E-9, 7.434353269016581E-9, 7.069508205296277E-9 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceRealGenericMultipleThreads", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 1.2506417202814183E-8, "scoreError" : 1.6795536311166225E-10, "scoreConfidence" : [ 1.2338461839702521E-8, 1.2674372565925844E-8 ], "scorePercentiles" : { "0.0" : 1.1733735859924257E-8, "50.0" : 1.255254892079249E-8, "90.0" : 1.2938445000457253E-8, "95.0" : 1.3008257332926023E-8, "99.0" : 1.3040048230071413E-8, "99.9" : 1.3040048230071413E-8, "99.99" : 1.3040048230071413E-8, "99.999" : 1.3040048230071413E-8, "99.9999" : 1.3040048230071413E-8, "100.0" : 1.3040048230071413E-8 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 1.2450580080092053E-8, 1.2111996004636375E-8, 1.2770603940132117E-8, 1.2648063423437725E-8, 1.2715763471008417E-8 ], [ 1.2326706782442042E-8, 1.2210799128178248E-8, 1.2835878531711753E-8, 1.2807023702810926E-8, 1.2733071615624519E-8 ], [ 1.2141135447130916E-8, 1.2140364012378727E-8, 1.2502036942453608E-8, 1.2744748833097659E-8, 1.2640977566390641E-8 ], [ 1.2047673738541486E-8, 1.184647947048693E-8, 1.2332669708897852E-8, 1.2398736798383E-8, 1.2556915935981821E-8 ], [ 1.1976943736552175E-8, 1.2067670427334915E-8, 1.2354869791770663E-8, 1.2596231099714954E-8, 1.267292838332715E-8 ], [ 1.228620296228141E-8, 1.212459517154207E-8, 1.2487777035882146E-8, 1.283045661418847E-8, 1.2839408073421376E-8 ], [ 1.2573329908719346E-8, 1.2226931345407104E-8, 1.2798375934137093E-8, 1.3012109822244077E-8, 1.2984828746834288E-8 ], [ 1.2316015858909192E-8, 1.1733735859924257E-8, 1.238178824599642E-8, 1.2548181905603159E-8, 1.2590116433774484E-8 ], [ 1.300510529621125E-8, 1.2511845228551519E-8, 1.2907209592671529E-8, 1.3040048230071413E-8, 1.2940719866531116E-8 ], [ 1.2244654726902586E-8, 1.1852327279427286E-8, 1.2917971205792497E-8, 1.2705604809180103E-8, 1.283065141398832E-8 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceRealGenericSingleThread", "mode" : "thrpt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 7.111649207987639E-9, "scoreError" : 3.776570942995927E-11, "scoreConfidence" : [ 7.07388349855768E-9, 7.149414917417598E-9 ], "scorePercentiles" : { "0.0" : 6.823621037283706E-9, "50.0" : 7.135597966802026E-9, "90.0" : 7.182100241676107E-9, "95.0" : 7.191592508239966E-9, "99.0" : 7.199594725453431E-9, "99.9" : 7.199594725453431E-9, "99.99" : 7.199594725453431E-9, "99.999" : 7.199594725453431E-9, "99.9999" : 7.199594725453431E-9, "100.0" : 7.199594725453431E-9 }, "scoreUnit" : "ops/ns", "rawData" : [ [ 7.138928928092329E-9, 7.1410786827150655E-9, 7.137501518592667E-9, 7.13255860706166E-9, 7.144644554308517E-9 ], [ 7.119680345718008E-9, 7.131499453153948E-9, 7.134950063786632E-9, 7.130702039926015E-9, 7.1465648381229144E-9 ], [ 7.125470657265963E-9, 7.125209697036681E-9, 7.133010435919711E-9, 7.129294481248521E-9, 7.136765881316877E-9 ], [ 7.143899342093931E-9, 7.1448114720701125E-9, 7.1163770023688E-9, 7.1248457364025385E-9, 7.145184731440814E-9 ], [ 7.142434183210029E-9, 7.13171417693858E-9, 7.140614766676195E-9, 7.128241034663117E-9, 7.140839759988699E-9 ], [ 7.021947224199773E-9, 6.832459851331237E-9, 6.980876182893762E-9, 6.823621037283706E-9, 7.13664931570586E-9 ], [ 7.189978942403097E-9, 7.187344901869864E-9, 7.193564644262807E-9, 7.185245749555078E-9, 7.199594725453431E-9 ], [ 7.084327504316193E-9, 7.153790670765373E-9, 7.126553173581471E-9, 7.128335514143629E-9, 7.13624586981742E-9 ], [ 7.016717081106459E-9, 7.025470698237944E-9, 7.016135450466004E-9, 7.017009003526007E-9, 7.0254070773336175E-9 ], [ 7.151812521170762E-9, 7.142951716813401E-9, 7.1471777841256826E-9, 7.144842075675615E-9, 7.147579293225488E-9 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.constantExpressionCompiled", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 4.212678763261015, "scoreError" : 0.0076870053824087976, "scoreConfidence" : [ 4.204991757878606, 4.220365768643424 ], "scorePercentiles" : { "0.0" : 4.185243124004843, "50.0" : 4.210598222929628, "90.0" : 4.237546820672852, "95.0" : 4.238835417400423, "99.0" : 4.246978960925418, "99.9" : 4.246978960925418, "99.99" : 4.246978960925418, "99.999" : 4.246978960925418, "99.9999" : 4.246978960925418, "100.0" : 4.246978960925418 }, "scoreUnit" : "ns/op", "rawData" : [ [ 4.2020899286376485, 4.211640233831468, 4.210403879702845, 4.2104764237877434, 4.239179487715927 ], [ 4.202757412135211, 4.213716586341822, 4.206633168069475, 4.208667416018454, 4.211235707887466 ], [ 4.196105407813176, 4.185243124004843, 4.18563796823676, 4.188200593541672, 4.200358290940981 ], [ 4.198480788267704, 4.203948520004001, 4.202704690366885, 4.200732046354101, 4.197315675942634 ], [ 4.202971926151868, 4.206603909714903, 4.200794148364205, 4.19659696653266, 4.198762981816232 ], [ 4.246978960925418, 4.226637105617118, 4.236677771731939, 4.2239113125158525, 4.2293939978697255 ], [ 4.221173309377198, 4.218562137223888, 4.2367399598105155, 4.217504433569328, 4.218855552494926 ], [ 4.197184582182064, 4.19860460400435, 4.210720022071511, 4.206636777715365, 4.197516693638447 ], [ 4.212663876955742, 4.210796948610319, 4.2191925897528595, 4.220974820828683, 4.2188381091381775 ], [ 4.238553905324101, 4.2378493765499385, 4.23348800799394, 4.237636471879779, 4.235589553088881 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.constantExpressionInterpreted", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 23.476159513605285, "scoreError" : 0.044536418857247795, "scoreConfidence" : [ 23.431623094748037, 23.520695932462534 ], "scorePercentiles" : { "0.0" : 23.385692680760858, "50.0" : 23.44218642376611, "90.0" : 23.64765528951581, "95.0" : 23.662355635255032, "99.0" : 23.761374877781773, "99.9" : 23.761374877781773, "99.99" : 23.761374877781773, "99.999" : 23.761374877781773, "99.9999" : 23.761374877781773, "100.0" : 23.761374877781773 }, "scoreUnit" : "ns/op", "rawData" : [ [ 23.437719141044496, 23.44426895298397, 23.480234187222436, 23.44754756586917, 23.43497422806599 ], [ 23.64767057645583, 23.508624015993814, 23.528581213525456, 23.521798411485232, 23.52245142633455 ], [ 23.392906986061632, 23.391612935470246, 23.45092431410603, 23.761374877781773, 23.42584728096977 ], [ 23.429753877299877, 23.470297930862348, 23.428953816324803, 23.47033470342424, 23.4385348719052 ], [ 23.656122910002, 23.628020722264882, 23.66382803250812, 23.64751770705563, 23.661150946593413 ], [ 23.406550364147353, 23.43144560300939, 23.445786148660567, 23.405695056438308, 23.425412420656603 ], [ 23.414575594188914, 23.442710639342533, 23.41334470427381, 23.385692680760858, 23.38753238131 ], [ 23.43375923216899, 23.470217323817508, 23.409862349102834, 23.453153382549623, 23.41104635513602 ], [ 23.441662208189687, 23.4274144794497, 23.467206642677716, 23.42766696960862, 23.390157631894077 ], [ 23.466764198720917, 23.407530622932857, 23.490985461033954, 23.43057244951783, 23.630179149065007 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationAndComparisonExpressionCompiled", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 5.07369916848029, "scoreError" : 0.009036842912051984, "scoreConfidence" : [ 5.0646623255682375, 5.082736011392342 ], "scorePercentiles" : { "0.0" : 5.04760426869117, "50.0" : 5.069561009715118, "90.0" : 5.098406105564626, "95.0" : 5.0990478303194315, "99.0" : 5.112493842928659, "99.9" : 5.112493842928659, "99.99" : 5.112493842928659, "99.999" : 5.112493842928659, "99.9999" : 5.112493842928659, "100.0" : 5.112493842928659 }, "scoreUnit" : "ns/op", "rawData" : [ [ 5.04760426869117, 5.050299977306783, 5.05475569576501, 5.051356900346964, 5.050888484056435 ], [ 5.097922717330408, 5.092853207741855, 5.097216797997191, 5.0921813526635304, 5.0898819058200315 ], [ 5.075660710123181, 5.056595761473845, 5.062256815325732, 5.056290328044339, 5.058531981487619 ], [ 5.098451893111682, 5.112493842928659, 5.098988841245382, 5.094915272671527, 5.098618826763342 ], [ 5.082909718675992, 5.097727382553299, 5.083621401362909, 5.079458857211658, 5.08802028145316 ], [ 5.067355528985504, 5.056138298678848, 5.068283848405462, 5.06438370397046, 5.063853174365042 ], [ 5.055441300623839, 5.059742644280677, 5.059122306107231, 5.060582693187998, 5.0588583300620105 ], [ 5.058463555883924, 5.054646354395205, 5.050841735843806, 5.055633437779259, 5.054097688123031 ], [ 5.077858967818933, 5.07130798920343, 5.062256366519151, 5.070838171024772, 5.075879731685449 ], [ 5.091265891892027, 5.092769084484789, 5.099119928076602, 5.088720452824153, 5.097994017641121 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationAndComparisonExpressionInterpreted", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 702.3919660551082, "scoreError" : 5.681206542356396, "scoreConfidence" : [ 696.7107595127518, 708.0731725974647 ], "scorePercentiles" : { "0.0" : 690.7375328301506, "50.0" : 699.9639456987029, "90.0" : 721.8959942243439, "95.0" : 723.55435278019, "99.0" : 744.0124848709371, "99.9" : 744.0124848709371, "99.99" : 744.0124848709371, "99.999" : 744.0124848709371, "99.9999" : 744.0124848709371, "100.0" : 744.0124848709371 }, "scoreUnit" : "ns/op", "rawData" : [ [ 699.2650625406392, 702.0580899581502, 702.2363107738172, 707.4172629253029, 703.7206182778015 ], [ 703.5020836776823, 709.7473275693228, 705.2556980966034, 701.9411327916094, 701.2391167555148 ], [ 696.344307953545, 692.3685044504716, 690.7375328301506, 693.4217671369353, 716.0056453717223 ], [ 708.7312220432993, 744.0124848709371, 719.23466011605, 696.8805025311548, 693.8751126512857 ], [ 721.5951875253407, 722.8113309337571, 721.9294171908998, 722.4126619663224, 724.462490592497 ], [ 691.8527362069032, 692.1467071141449, 692.7012289900799, 697.6634191018752, 692.9437190295654 ], [ 692.7882188491903, 692.3194417428853, 693.7902455554018, 692.0302999692109, 693.2198570058273 ], [ 704.0832457154596, 708.2716009254125, 709.0992807989484, 703.6873112861232, 700.0317954263496 ], [ 691.0228553011165, 691.5934019848671, 691.2356113684625, 691.8243006234165, 691.9514995113767 ], [ 697.8796389475384, 699.7763092947165, 702.281921019001, 699.8960959710561, 704.302029485678 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationExpressionCompiled", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 5.483375422308572, "scoreError" : 0.011179774091267526, "scoreConfidence" : [ 5.472195648217304, 5.49455519639984 ], "scorePercentiles" : { "0.0" : 5.454717662055155, "50.0" : 5.478503375701898, "90.0" : 5.510791394319896, "95.0" : 5.525800689784233, "99.0" : 5.57335200131931, "99.9" : 5.57335200131931, "99.99" : 5.57335200131931, "99.999" : 5.57335200131931, "99.9999" : 5.57335200131931, "100.0" : 5.57335200131931 }, "scoreUnit" : "ns/op", "rawData" : [ [ 5.478416395688247, 5.467756441963081, 5.480135666854784, 5.487164164793854, 5.477951490186059 ], [ 5.5172353143887065, 5.494813376902206, 5.477605862557764, 5.475473382864657, 5.467745502333391 ], [ 5.467643372275811, 5.47088980487035, 5.459473250685656, 5.464084472855917, 5.4662617755223835 ], [ 5.462501151534489, 5.463138277177212, 5.45954915364299, 5.465125859487712, 5.457160184861723 ], [ 5.494529047930599, 5.474820377345847, 5.487163014509654, 5.484448634917844, 5.47859035571555 ], [ 5.4923066615921385, 5.490320751774397, 5.490603522637868, 5.57335200131931, 5.4934859296889815 ], [ 5.5167436412905815, 5.51035701160504, 5.510839659065991, 5.500752535112931, 5.509508374452085 ], [ 5.473603139672041, 5.486192107233305, 5.4720789382484964, 5.472020173180854, 5.490956351377059 ], [ 5.459342896268209, 5.45896417792104, 5.463191385526211, 5.457629732273535, 5.454717662055155 ], [ 5.497347689076424, 5.489147274417496, 5.491621799748753, 5.53626948193432, 5.497741886089842 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationExpressionInterpreted", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 679.1732435698752, "scoreError" : 8.548577193783828, "scoreConfidence" : [ 670.6246663760915, 687.721820763659 ], "scorePercentiles" : { "0.0" : 665.7810106687843, "50.0" : 678.085026629705, "90.0" : 689.4791699537317, "95.0" : 708.0428738108368, "99.0" : 779.4931702277812, "99.9" : 779.4931702277812, "99.99" : 779.4931702277812, "99.999" : 779.4931702277812, "99.9999" : 779.4931702277812, "100.0" : 779.4931702277812 }, "scoreUnit" : "ns/op", "rawData" : [ [ 678.3639912178666, 678.0584609814931, 679.6652154752525, 678.1115922779169, 679.6734788179346 ], [ 679.4227971029111, 679.885893024413, 679.0047881058658, 682.0002584427766, 705.9136309246767 ], [ 671.1510834427932, 665.8024437247665, 667.0842622401946, 710.6452817828102, 779.4931702277812 ], [ 668.3074083997186, 668.5026652754875, 667.0190218714176, 669.6054763997806, 675.8851982856378 ], [ 669.9683314378946, 671.0671255969364, 671.8514463133301, 672.5706480386556, 672.3573554506798 ], [ 666.3840441554436, 668.1974958845977, 666.3434175057929, 681.282796703046, 665.7810106687843 ], [ 668.8496086396843, 675.3729435938851, 668.3368609565813, 668.2072014861429, 670.8554007523696 ], [ 676.6288857823113, 675.8224381759746, 679.9581575963257, 679.6543145780016, 689.5452243360373 ], [ 680.0055673098983, 679.8027880870974, 679.5378429427511, 681.6638322582755, 680.1771952439908 ], [ 688.884680512981, 697.4584509497643, 682.6927045862542, 682.1795375103585, 683.6287534184123 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationWithMapExpressionCompiled", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 8.123414308199202, "scoreError" : 0.014496378489671844, "scoreConfidence" : [ 8.10891792970953, 8.137910686688873 ], "scorePercentiles" : { "0.0" : 8.055515736850916, "50.0" : 8.129378978414803, "90.0" : 8.154097663510694, "95.0" : 8.161910446433415, "99.0" : 8.175349863209417, "99.9" : 8.175349863209417, "99.99" : 8.175349863209417, "99.999" : 8.175349863209417, "99.9999" : 8.175349863209417, "100.0" : 8.175349863209417 }, "scoreUnit" : "ns/op", "rawData" : [ [ 8.124432278617306, 8.119729352321361, 8.100064744515407, 8.106666919659666, 8.105609959339107 ], [ 8.129695672034202, 8.132619758061143, 8.133018506321315, 8.124950507028247, 8.1290622847954 ], [ 8.098671628979277, 8.10781423647921, 8.0903852678503, 8.114732865670165, 8.08607827441062 ], [ 8.126620400790996, 8.150363001658617, 8.13533573484541, 8.153970607766869, 8.143369192699273 ], [ 8.10824525171707, 8.100208441142836, 8.10974742786035, 8.092451336766851, 8.101116363003937 ], [ 8.059346831954553, 8.0655584348832, 8.067207000816227, 8.06624156387142, 8.055515736850916 ], [ 8.133713413488563, 8.134503804910993, 8.132283377431015, 8.125571127709891, 8.137233326766927 ], [ 8.12488782736298, 8.175349863209417, 8.145227424852688, 8.15977518952051, 8.141910771050837 ], [ 8.154111337954895, 8.153166417173763, 8.140124663592726, 8.159573111000894, 8.141141486917757 ], [ 8.152550420013691, 8.150125348755626, 8.153974593512878, 8.164520204882525, 8.152142117140281 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.propertyNavigationWithMapExpressionInterpreted", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 857.1613925870099, "scoreError" : 6.1584363747836495, "scoreConfidence" : [ 851.0029562122263, 863.3198289617935 ], "scorePercentiles" : { "0.0" : 824.1896519970318, "50.0" : 859.0435453842059, "90.0" : 870.0590452850839, "95.0" : 874.6331142506676, "99.0" : 889.2012620887944, "99.9" : 889.2012620887944, "99.99" : 889.2012620887944, "99.999" : 889.2012620887944, "99.9999" : 889.2012620887944, "100.0" : 889.2012620887944 }, "scoreUnit" : "ns/op", "rawData" : [ [ 855.3559220712914, 853.8970487290958, 856.9446457251869, 849.2515874296292, 856.5624886821846 ], [ 834.776135547951, 826.0634199181575, 832.5656255228541, 832.9911861233393, 824.1896519970318 ], [ 868.093714320431, 860.1584182768488, 858.6024309549726, 861.3180009714056, 862.8909122987285 ], [ 848.4252112980126, 845.8224323250091, 853.2126812481285, 844.1887423101741, 848.1008693159945 ], [ 865.7778282736472, 868.2777215890987, 870.9266380386422, 865.9331097250554, 869.5330335734542 ], [ 860.5725018013658, 851.052833198961, 852.565048739831, 848.455051876433, 868.3813106832996 ], [ 847.0413481762466, 879.1632518431433, 859.022092509916, 860.9451800804145, 860.4472667315074 ], [ 870.1174910308206, 858.6033461043527, 859.7857985306987, 858.7603197997023, 866.4357938225781 ], [ 856.6059854784088, 858.0700290661762, 889.2012620887944, 859.0649982584958, 859.3523779738366 ], [ 862.3928445639077, 870.6837174177514, 864.356715723321, 862.9793908990348, 860.1542166851717 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.singlePropertyExpressionCompiled", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 2.3412293359031016, "scoreError" : 0.00909435391011207, "scoreConfidence" : [ 2.3321349819929895, 2.3503236898132136 ], "scorePercentiles" : { "0.0" : 2.3168019116446423, "50.0" : 2.33767011827742, "90.0" : 2.3609834546224193, "95.0" : 2.366910589781843, "99.0" : 2.4318742354614042, "99.9" : 2.4318742354614042, "99.99" : 2.4318742354614042, "99.999" : 2.4318742354614042, "99.9999" : 2.4318742354614042, "100.0" : 2.4318742354614042 }, "scoreUnit" : "ns/op", "rawData" : [ [ 2.3393401896393256, 2.352886325314782, 2.3330290857970715, 2.334662682351483, 2.3311274400574136 ], [ 2.3279232412078503, 2.3369981439312997, 2.3257155770675957, 2.344021270214791, 2.3228157502480338 ], [ 2.345455436671617, 2.3611110403224322, 2.348068337675366, 2.336909179733739, 2.342048510772919 ], [ 2.3520320923572524, 2.3513075535982977, 2.362925664000496, 2.361285809744925, 2.3548328382971033 ], [ 2.3292870894847333, 2.335195001565128, 2.3598351833223044, 2.351132652701361, 2.4318742354614042 ], [ 2.3428433518994454, 2.33834209262354, 2.3398609370931025, 2.3717810546257123, 2.3490933202108555 ], [ 2.3179279541618354, 2.329418614102727, 2.3171947668886332, 2.3319851387917336, 2.3175789339721833 ], [ 2.3397369370583183, 2.352448204968322, 2.329058560926048, 2.326727557644771, 2.3168019116446423 ], [ 2.330778738199022, 2.330013084244065, 2.331542908246752, 2.356940028414262, 2.33415910374694 ], [ 2.3321867135896714, 2.343748112153039, 2.333328098276298, 2.3408391058443643, 2.335311234290065 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlPerformanceBenchmarks.singlePropertyExpressionInterpreted", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 2, "primaryMetric" : { "score" : 162.06714052857922, "scoreError" : 0.8006111061718928, "scoreConfidence" : [ 161.26652942240733, 162.8677516347511 ], "scorePercentiles" : { "0.0" : 160.1723191047914, "50.0" : 161.48334465069073, "90.0" : 165.11008181336098, "95.0" : 165.26375636661982, "99.0" : 165.35405811829082, "99.9" : 165.35405811829082, "99.99" : 165.35405811829082, "99.999" : 165.35405811829082, "99.9999" : 165.35405811829082, "100.0" : 165.35405811829082 }, "scoreUnit" : "ns/op", "rawData" : [ [ 165.20713270369404, 165.35405811829082, 165.27406720459737, 165.23177127685847, 165.25532022645635 ], [ 164.19986124474886, 164.2366238003634, 164.17355573622413, 164.17962421687227, 164.20656514993556 ], [ 160.48547954366927, 160.8562771288926, 160.6731879498754, 160.84385883318603, 160.67842299853774 ], [ 162.77890393112065, 162.6902141464944, 162.66825231563274, 162.81823424435368, 162.76641983321085 ], [ 160.42689581737193, 160.45428784635138, 160.67686772595573, 160.4683314489738, 160.47459045832454 ], [ 160.9269110802522, 161.00911859730383, 161.16780133265848, 161.3137281498279, 161.04527213008535 ], [ 161.65296115155354, 161.86575069491292, 161.82319740908474, 161.93481585196466, 161.9292005925734 ], [ 160.20944631534792, 160.24524424439878, 160.1956322730681, 160.32405683749238, 160.1723191047914 ], [ 162.92168148271045, 163.05162613955872, 162.9977757147687, 162.82511260915683, 162.97704583256134 ], [ 161.10395123822926, 161.09020807969563, 161.1916229355633, 161.14689472034425, 161.15684801106391 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceFakeGenericMultipleThreads", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 8.352387326354313E7, "scoreError" : 1273944.5565008267, "scoreConfidence" : [ 8.22499287070423E7, 8.479781782004395E7 ], "scorePercentiles" : { "0.0" : 7.994802038461539E7, "50.0" : 8.320792673076923E7, "90.0" : 8.671046423333333E7, "95.0" : 8.853908885833333E7, "99.0" : 9.119983481818181E7, "99.9" : 9.119983481818181E7, "99.99" : 9.119983481818181E7, "99.999" : 9.119983481818181E7, "99.9999" : 9.119983481818181E7, "100.0" : 9.119983481818181E7 }, "scoreUnit" : "ns/op", "rawData" : [ [ 8.245578976923077E7, 8.464437175E7, 8.073084523076923E7, 8.089255353846154E7, 8.038750315384616E7 ], [ 8.34967295E7, 8.569674533333333E7, 8.075474607692307E7, 8.008213453846154E7, 8.03252733076923E7 ], [ 8.598181791666667E7, 8.48831675E7, 8.452698483333333E7, 8.279297038461539E7, 8.360238358333333E7 ], [ 8.297315207692307E7, 8.5976206E7, 8.2337632E7, 8.164322353846154E7, 8.210704553846154E7 ], [ 8.6278332E7, 8.574380575E7, 8.517334975E7, 8.33412826923077E7, 8.24347216923077E7 ], [ 8.45245365E7, 8.5520408E7, 8.307457076923077E7, 8.170058653846154E7, 8.195896215384616E7 ], [ 8.5783988E7, 8.666831933333333E7, 8.06874376923077E7, 8.025599423076923E7, 7.994802038461539E7 ], [ 9.119983481818181E7, 8.6715147E7, 8.828961508333333E7, 8.676237308333333E7, 8.424843566666667E7 ], [ 8.114361007692307E7, 8.471947416666667E7, 8.08341006923077E7, 8.045492992307693E7, 8.149901953846154E7 ], [ 8.364378158333333E7, 8.884400125E7, 8.477130008333333E7, 8.218445253846154E7, 8.149799661538461E7 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceFakeGenericSingleThread", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 1.4467023570000005E8, "scoreError" : 574219.4823169827, "scoreConfidence" : [ 1.4409601621768308E8, 1.4524445518231702E8 ], "scorePercentiles" : { "0.0" : 1.434304227142857E8, "50.0" : 1.439757837142857E8, "90.0" : 1.4642288445714286E8, "95.0" : 1.4662256341428572E8, "99.0" : 1.4691930257142857E8, "99.9" : 1.4691930257142857E8, "99.99" : 1.4691930257142857E8, "99.999" : 1.4691930257142857E8, "99.9999" : 1.4691930257142857E8, "100.0" : 1.4691930257142857E8 }, "scoreUnit" : "ns/op", "rawData" : [ [ 1.446627332857143E8, 1.447269092857143E8, 1.4477477342857143E8, 1.448543292857143E8, 1.445648752857143E8 ], [ 1.4436442842857143E8, 1.439379082857143E8, 1.4380489914285713E8, 1.4360596085714287E8, 1.434304227142857E8 ], [ 1.4360466685714287E8, 1.43754466E8, 1.4429112857142857E8, 1.4387157285714287E8, 1.4353923757142857E8 ], [ 1.4372011985714287E8, 1.43881248E8, 1.43845029E8, 1.439033427142857E8, 1.439665527142857E8 ], [ 1.463655302857143E8, 1.4621592142857143E8, 1.4617292414285713E8, 1.4630755057142857E8, 1.4683932957142857E8 ], [ 1.4384283185714287E8, 1.43774449E8, 1.439850147142857E8, 1.439146122857143E8, 1.438450832857143E8 ], [ 1.4631791357142857E8, 1.4617162785714287E8, 1.46170411E8, 1.4609647042857143E8, 1.45977197E8 ], [ 1.436695E8, 1.4432131185714287E8, 1.4345975214285713E8, 1.4354633614285713E8, 1.4347295485714287E8 ], [ 1.4412936114285713E8, 1.437527887142857E8, 1.438538882857143E8, 1.4382016714285713E8, 1.438821697142857E8 ], [ 1.464452092857143E8, 1.4642925714285713E8, 1.4644387642857143E8, 1.4626443842857143E8, 1.4691930257142857E8 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceMultipleClassesMultipleMethodsMultipleThreads", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 2.71095473096E9, "scoreError" : 1.9977270327970363E7, "scoreConfidence" : [ 2.6909774606320295E9, 2.7309320012879705E9 ], "scorePercentiles" : { "0.0" : 2.638522652E9, "50.0" : 2.7108129845E9, "90.0" : 2.7709087037E9, "95.0" : 2.7836798922E9, "99.0" : 2.798246682E9, "99.9" : 2.798246682E9, "99.99" : 2.798246682E9, "99.999" : 2.798246682E9, "99.9999" : 2.798246682E9, "100.0" : 2.798246682E9 }, "scoreUnit" : "ns/op", "rawData" : [ [ 2.688984779E9, 2.682222232E9, 2.6890379E9, 2.704055534E9, 2.638522652E9 ], [ 2.672001801E9, 2.645016804E9, 2.667737701E9, 2.686371017E9, 2.660283669E9 ], [ 2.731206109E9, 2.685468942E9, 2.69960018E9, 2.665116238E9, 2.670310687E9 ], [ 2.779242813E9, 2.721287568E9, 2.710086344E9, 2.724530485E9, 2.740310298E9 ], [ 2.692422176E9, 2.726433823E9, 2.706659761E9, 2.720219243E9, 2.713869161E9 ], [ 2.754523352E9, 2.771792487E9, 2.776570864E9, 2.798246682E9, 2.789102989E9 ], [ 2.689571445E9, 2.66818863E9, 2.668514183E9, 2.675913198E9, 2.643841035E9 ], [ 2.733076795E9, 2.743385479E9, 2.744937143E9, 2.737885114E9, 2.746627202E9 ], [ 2.73916636E9, 2.742518429E9, 2.736624248E9, 2.762954654E9, 2.755123304E9 ], [ 2.691088842E9, 2.71968694E9, 2.666183664E9, 2.659675967E9, 2.711539625E9 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceMultipleClassesMultipleMethodsSingleThread", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 7.44992529988E9, "scoreError" : 7.047273836486752E7, "scoreConfidence" : [ 7.379452561515133E9, 7.520398038244867E9 ], "scorePercentiles" : { "0.0" : 7.359428002E9, "50.0" : 7.406071921E9, "90.0" : 7.8256503857E9, "95.0" : 7.86690699055E9, "99.0" : 7.880369414E9, "99.9" : 7.880369414E9, "99.99" : 7.880369414E9, "99.999" : 7.880369414E9, "99.9999" : 7.880369414E9, "100.0" : 7.880369414E9 }, "scoreUnit" : "ns/op", "rawData" : [ [ 7.42751174E9, 7.401949743E9, 7.412955761E9, 7.489817687E9, 7.39897997E9 ], [ 7.405322833E9, 7.399841686E9, 7.404428314E9, 7.404302674E9, 7.3983849E9 ], [ 7.401504853E9, 7.398063978E9, 7.420953094E9, 7.404078886E9, 7.402113812E9 ], [ 7.41598624E9, 7.40910997E9, 7.414776221E9, 7.411710684E9, 7.412316046E9 ], [ 7.865024344E9, 7.864842668E9, 7.880369414E9, 7.869208003E9, 7.86296513E9 ], [ 7.400377489E9, 7.402528686E9, 7.407396522E9, 7.399918285E9, 7.400009565E9 ], [ 7.416153826E9, 7.410083396E9, 7.406821009E9, 7.413669386E9, 7.407543862E9 ], [ 7.413344685E9, 7.411399649E9, 7.416402764E9, 7.426276173E9, 7.414437591E9 ], [ 7.359428002E9, 7.363959737E9, 7.362285279E9, 7.362000555E9, 7.362184745E9 ], [ 7.392767666E9, 7.394700391E9, 7.393200034E9, 7.391291488E9, 7.391565558E9 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceNonGenericSingleThread", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 3.39755759785E8, "scoreError" : 1713782.2165799108, "scoreConfidence" : [ 3.380419775684201E8, 3.4146954200157994E8 ], "scorePercentiles" : { "0.0" : 3.3342244325E8, "50.0" : 3.38800609E8, "90.0" : 3.448974335E8, "95.0" : 3.4540848548333335E8, "99.0" : 3.459205973333333E8, "99.9" : 3.459205973333333E8, "99.99" : 3.459205973333333E8, "99.999" : 3.459205973333333E8, "99.9999" : 3.459205973333333E8, "100.0" : 3.459205973333333E8 }, "scoreUnit" : "ns/op", "rawData" : [ [ 3.41047206E8, 3.38517581E8, 3.383519596666667E8, 3.378490023333333E8, 3.387698476666667E8 ], [ 3.430251446666667E8, 3.44651066E8, 3.437956203333333E8, 3.43890735E8, 3.42871645E8 ], [ 3.379876736666667E8, 3.367303413333333E8, 3.36498448E8, 3.390440733333333E8, 3.383978636666667E8 ], [ 3.34431708E8, 3.406872243333333E8, 3.347289073333333E8, 3.347630116666667E8, 3.3342244325E8 ], [ 3.404434036666667E8, 3.383636973333333E8, 3.382002256666667E8, 3.37626299E8, 3.370521846666667E8 ], [ 3.39794674E8, 3.38749805E8, 3.388313703333333E8, 3.40570021E8, 3.395010633333333E8 ], [ 3.434053303333333E8, 3.428932616666667E8, 3.43542987E8, 3.44707346E8, 3.441448436666667E8 ], [ 3.384047183333333E8, 3.385700056666667E8, 3.37526489E8, 3.38927356E8, 3.366965083333333E8 ], [ 3.349973976666667E8, 3.35112794E8, 3.3535672E8, 3.364920316666667E8, 3.415010343333333E8 ], [ 3.455425646666667E8, 3.452344186666667E8, 3.452987843333333E8, 3.459205973333333E8, 3.449185543333333E8 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceNotGenericMultipleThreads", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 1.3942383400392857E8, "scoreError" : 1144451.0748553728, "scoreConfidence" : [ 1.3827938292907318E8, 1.4056828507878396E8 ], "scorePercentiles" : { "0.0" : 1.3491269025E8, "50.0" : 1.391181768125E8, "90.0" : 1.4255624765E8, "95.0" : 1.4442842889285713E8, "99.0" : 1.457412137142857E8, "99.9" : 1.457412137142857E8, "99.99" : 1.457412137142857E8, "99.999" : 1.457412137142857E8, "99.9999" : 1.457412137142857E8, "100.0" : 1.457412137142857E8 }, "scoreUnit" : "ns/op", "rawData" : [ [ 1.3772884725E8, 1.4430199657142857E8, 1.3575141975E8, 1.39316399125E8, 1.38799587875E8 ], [ 1.39464474375E8, 1.42602369625E8, 1.38559932125E8, 1.41896449125E8, 1.40164175375E8 ], [ 1.3491269025E8, 1.38739415E8, 1.40050271875E8, 1.39054476375E8, 1.38588054125E8 ], [ 1.38781422E8, 1.3798120975E8, 1.3918187725E8, 1.37625942E8, 1.4071597475E8 ], [ 1.3952694675E8, 1.369029375E8, 1.40834021375E8, 1.36386410625E8, 1.40892485375E8 ], [ 1.41931377E8, 1.384901045E8, 1.4164807725E8, 1.38699281125E8, 1.457412137142857E8 ], [ 1.39720232125E8, 1.40247401E8, 1.4263733075E8, 1.412396035E8, 1.445829572857143E8 ], [ 1.379924135E8, 1.4037367275E8, 1.37717747375E8, 1.40651737E8, 1.37831337375E8 ], [ 1.36645853625E8, 1.40882177125E8, 1.368384205E8, 1.3738314525E8, 1.39629705E8 ], [ 1.35363586125E8, 1.38401413125E8, 1.3740978025E8, 1.38232196875E8, 1.42141149875E8 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceRealGenericMultipleThreads", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 8.053043351829672E7, "scoreError" : 1351715.494013605, "scoreConfidence" : [ 7.917871802428311E7, 8.188214901231033E7 ], "scorePercentiles" : { "0.0" : 7.304900614285715E7, "50.0" : 8.02318536923077E7, "90.0" : 8.487123913333333E7, "95.0" : 8.58002158875E7, "99.0" : 8.745828533333333E7, "99.9" : 8.745828533333333E7, "99.99" : 8.745828533333333E7, "99.999" : 8.745828533333333E7, "99.9999" : 8.745828533333333E7, "100.0" : 8.745828533333333E7 }, "scoreUnit" : "ns/op", "rawData" : [ [ 8.11243483076923E7, 8.251331923076923E7, 8.0575296E7, 7.961048E7, 7.955703407692307E7 ], [ 8.27230833076923E7, 8.189102884615384E7, 8.469158008333333E7, 8.078371461538461E7, 8.114396415384616E7 ], [ 8.2539305E7, 8.578243275E7, 8.04349396923077E7, 8.0612793E7, 7.927103261538461E7 ], [ 7.610691971428572E7, 7.88668166923077E7, 7.663702607142857E7, 7.451366842857143E7, 7.304900614285715E7 ], [ 8.013191715384616E7, 8.1782115E7, 7.960148361538461E7, 7.982062346153846E7, 7.889315823076923E7 ], [ 8.157060261538461E7, 8.745828533333333E7, 7.98424096923077E7, 8.033179023076923E7, 7.94001643076923E7 ], [ 8.046160207692307E7, 8.082731192307693E7, 7.921476246153846E7, 7.85831743076923E7, 7.837727253846154E7 ], [ 8.09104543076923E7, 8.566713466666667E7, 7.884170653846154E7, 7.909124615384616E7, 7.900141415384616E7 ], [ 8.582195083333333E7, 8.489120125E7, 8.171101084615384E7, 7.885028553846154E7, 7.991055676923077E7 ], [ 8.258894576923077E7, 8.337952833333333E7, 7.899374407692307E7, 7.849319761538461E7, 7.964483738461539E7 ] ] }, "secondaryMetrics" : { } }, { "jmhVersion" : "1.37", "benchmark" : "ognl.benchmarks.OgnlRuntimePerformanceBenchmarks.testPerformanceRealGenericSingleThread", "mode" : "avgt", "threads" : 1, "forks" : 10, "jvm" : "/usr/lib/jvm/temurin-17-jdk-amd64/bin/java", "jvmArgs" : [ "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ], "jdkVersion" : "17.0.18", "vmName" : "OpenJDK 64-Bit Server VM", "vmVersion" : "17.0.18+8", "warmupIterations" : 3, "warmupTime" : "1 s", "warmupBatchSize" : 1, "measurementIterations" : 5, "measurementTime" : "1 s", "measurementBatchSize" : 1, "primaryMetric" : { "score" : 1.400855760925E8, "scoreError" : 556865.6762001081, "scoreConfidence" : [ 1.3952871041629988E8, 1.4064244176870012E8 ], "scorePercentiles" : { "0.0" : 1.38672728875E8, "50.0" : 1.402176915625E8, "90.0" : 1.424592530875E8, "95.0" : 1.4275894314375E8, "99.0" : 1.42916628375E8, "99.9" : 1.42916628375E8, "99.99" : 1.42916628375E8, "99.999" : 1.42916628375E8, "99.9999" : 1.42916628375E8, "100.0" : 1.42916628375E8 }, "scoreUnit" : "ns/op", "rawData" : [ [ 1.40203864375E8, 1.40306679125E8, 1.39804513875E8, 1.4025443125E8, 1.4011435875E8 ], [ 1.3921078E8, 1.3929364225E8, 1.39126003875E8, 1.39425657625E8, 1.3944110875E8 ], [ 1.4064285375E8, 1.40675902625E8, 1.406477385E8, 1.40761277875E8, 1.40408688E8 ], [ 1.38965885125E8, 1.3881993E8, 1.38741238625E8, 1.390525345E8, 1.38936058875E8 ], [ 1.4011044075E8, 1.4016869725E8, 1.4023151875E8, 1.402507375E8, 1.40316498875E8 ], [ 1.42708035375E8, 1.4282116375E8, 1.42647917E8, 1.42916628375E8, 1.42673424375E8 ], [ 1.39386041375E8, 1.3905761575E8, 1.39091643625E8, 1.3887470075E8, 1.38672728875E8 ], [ 1.4055718575E8, 1.40444986125E8, 1.404350445E8, 1.40379124875E8, 1.40591166125E8 ], [ 1.38773988125E8, 1.3893956875E8, 1.39070526875E8, 1.390675925E8, 1.3884608775E8 ], [ 1.40399776625E8, 1.4047827875E8, 1.40751854875E8, 1.404207355E8, 1.40361947375E8 ] ] }, "secondaryMetrics" : { } } ] ================================================ FILE: benchmarks/pom.xml ================================================ 4.0.0 ognl ognl-parent 3.5.0-SNAPSHOT benchmarks OGNL Benchmarks JMH-based performance benchmarks for OGNL UTF-8 benchmarks ognl ognl ${project.version} org.openjdk.jmh jmh-core org.openjdk.jmh jmh-generator-annprocess provided com.fasterxml.jackson.core jackson-databind org.apache.maven.plugins maven-compiler-plugin org.openjdk.jmh jmh-generator-annprocess ${jmh.version} org.apache.maven.plugins maven-shade-plugin package shade ${benchmarks.finalName} false *:* META-INF/LICENSE META-INF/NOTICE META-INF/MANIFEST.MF META-INF/versions/**/module-info.class module-info.class ognl.Run org.apache.maven.plugins maven-source-plugin true true true attach-sources jar-no-fork org.apache.maven.plugins maven-javadoc-plugin none true true 17 https://docs.oracle.com/en/java/javase/17/docs/api/ none true UTF-8 attach-source jar org.apache.maven.plugins maven-deploy-plugin true benchmarks org.codehaus.mojo exec-maven-plugin exec exec verify java -jar ${project.build.directory}/${benchmarks.finalName}.jar ${benchmarks.jmhArgs} coverage org.sonarsource.scanner.maven sonar-maven-plugin true ================================================ FILE: benchmarks/src/main/java/ognl/DefaultMemberAccess.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Member; import java.lang.reflect.Modifier; /** * This class provides methods for setting up and restoring * access in a Field. Java 2 provides access utilities for setting * and getting fields that are non-public. This object provides * coarse-grained access controls to allow access to private, protected * and package protected members. This will apply to all classes * and members. */ public class DefaultMemberAccess implements MemberAccess { public boolean allowPrivateAccess; public boolean allowProtectedAccess; public boolean allowPackageProtectedAccess; public DefaultMemberAccess(boolean allowAllAccess) { this(allowAllAccess, allowAllAccess, allowAllAccess); } public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess) { super(); this.allowPrivateAccess = allowPrivateAccess; this.allowProtectedAccess = allowProtectedAccess; this.allowPackageProtectedAccess = allowPackageProtectedAccess; } public boolean getAllowPrivateAccess() { return allowPrivateAccess; } public void setAllowPrivateAccess(boolean value) { allowPrivateAccess = value; } public boolean getAllowProtectedAccess() { return allowProtectedAccess; } public void setAllowProtectedAccess(boolean value) { allowProtectedAccess = value; } public boolean getAllowPackageProtectedAccess() { return allowPackageProtectedAccess; } public void setAllowPackageProtectedAccess(boolean value) { allowPackageProtectedAccess = value; } public Object setup(OgnlContext context, Object target, Member member, String propertyName) { return null; } public void restore(OgnlContext context, Object target, Member member, String propertyName, Object state) { // no-op } /** * Returns true if the given member is accessible or can be made accessible * by this object. * * @param context the current execution context (not used). * @param target the Object to test accessibility for (not used). * @param member the Member to test accessibility for. * @param propertyName the property to test accessibility for (not used). * @return true if the member is accessible in the context, false otherwise. */ public boolean isAccessible(OgnlContext context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); boolean result = Modifier.isPublic(modifiers); if (!result) { if (Modifier.isPrivate(modifiers)) { result = getAllowPrivateAccess(); } else { if (Modifier.isProtected(modifiers)) { result = getAllowProtectedAccess(); } else { result = getAllowPackageProtectedAccess(); } } } return result; } } ================================================ FILE: benchmarks/src/main/java/ognl/Run.java ================================================ package ognl; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.openjdk.jmh.results.format.ResultFormatType; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Iterator; public class Run { private static final String BASELINE_FILE = "etc/ognl-runtime-benchmark-baseline.json"; private static final String RESULTS_FILE = "target/ognl-runtime-benchmark-results.json"; private static final double REGRESSION_THRESHOLD = 10.0; public static void main(String[] args) throws Exception { ChainedOptionsBuilder builder = new OptionsBuilder() .include("ognl.benchmarks.*") .resultFormat(ResultFormatType.JSON) .result(RESULTS_FILE); // Parse -f flag from command-line args to override @Fork annotation for (int i = 0; i < args.length; i++) { if ("-f".equals(args[i]) && i + 1 < args.length) { builder = builder.forks(Integer.parseInt(args[i + 1])); i++; } } Options opt = builder.build(); new Runner(opt).run(); String options = System.getenv("OPTIONS"); if (options != null && options.contains("generateBaseline")) { System.out.println("Baseline generation complete. Results written to " + RESULTS_FILE); return; } ObjectMapper mapper = new ObjectMapper(); JsonNode baseline = mapper.readTree(new File(BASELINE_FILE)); JsonNode current = mapper.readTree(new File(RESULTS_FILE)); int regressions; if (options != null && options.contains("publishSummary")) { System.out.println("Publishing Summary of comparing results with baseline"); regressions = publishSummary(baseline, current); } else { System.out.println("Comparing results with baseline"); regressions = compareResults(baseline, current); } if (regressions > 0) { System.err.printf("%d benchmark(s) regressed >%.0f%% — failing build%n", regressions, REGRESSION_THRESHOLD); System.exit(1); } } private static int compareResults(JsonNode baseline, JsonNode current) { int regressionCount = 0; for (JsonNode baseBench : baseline) { String benchmark = baseBench.get("benchmark").asText(); String mode = baseBench.get("mode").asText(); double baseScore = baseBench.get("primaryMetric").get("score").asDouble(); Iterator it = current.elements(); boolean found = false; while (it.hasNext()) { JsonNode currBench = it.next(); if (currBench.get("benchmark").asText().equals(benchmark) && currBench.get("mode").asText().equals(mode)) { double currScore = currBench.get("primaryMetric").get("score").asDouble(); double percent = (baseScore != 0) ? ((currScore - baseScore) / baseScore) * 100 : 0; boolean isRegression = isRegression(mode, percent); if (isRegression) regressionCount++; String flag = isRegression ? " <-- REGRESSION" : ""; System.out.printf("%s [%s]: baseline=%.3f, current=%.3f, diff=%.3f (%.1f%%)%s%n", benchmark, mode, baseScore, currScore, baseScore - currScore, percent, flag); found = true; } } if (!found) { System.out.printf("----- NOT FOUND -----%n"); System.out.printf("%s [%s]%n", benchmark, mode); System.out.printf("----- NOT FOUND -----%n"); } } return regressionCount; } private static int publishSummary(JsonNode baseline, JsonNode current) throws IOException { StringBuilder table = new StringBuilder(); table.append("| Benchmark | Mode | Baseline | Current | Diff | Change (%) | Status |\n"); table.append("|-----------|------|----------|---------|------|------------|--------|\n"); int regressionCount = 0; for (JsonNode baseBench : baseline) { String benchmark = baseBench.get("benchmark").asText(); String mode = baseBench.get("mode").asText(); double baseScore = baseBench.get("primaryMetric").get("score").asDouble(); Iterator it = current.elements(); boolean found = false; while (it.hasNext()) { JsonNode currBench = it.next(); if (currBench.get("benchmark").asText().equals(benchmark) && currBench.get("mode").asText().equals(mode)) { double currScore = currBench.get("primaryMetric").get("score").asDouble(); double diff = baseScore - currScore; double percent = (baseScore != 0) ? ((currScore - baseScore) / baseScore) * 100 : 0; boolean isRegression = isRegression(mode, percent); String status = isRegression ? ":warning:" : ":white_check_mark:"; if (isRegression) regressionCount++; table.append(String.format("| %s | %s | %.3f | %.3f | %.3f | %.1f%% | %s |\n", benchmark, mode, baseScore, currScore, diff, percent, status)); found = true; } } if (!found) { table.append(String.format("| %s | %s | %.3f | NOT FOUND | - | - | :grey_question: |\n", benchmark, mode, baseScore)); } } StringBuilder summary = new StringBuilder(); if (regressionCount == 0) { summary.append(":white_check_mark: **No significant regressions detected**\n\n"); } else { summary.append(String.format(":warning: **%d benchmark(s) regressed >%.0f%%**\n\n", regressionCount, REGRESSION_THRESHOLD)); } summary.append(table); String summaryPath = System.getenv("GITHUB_STEP_SUMMARY"); if (summaryPath != null) { Files.writeString( Paths.get(summaryPath), summary.toString(), StandardOpenOption.CREATE, StandardOpenOption.APPEND ); } return regressionCount; } private static boolean isRegression(String mode, double percentChange) { if ("thrpt".equals(mode)) { // Throughput: regression if current is >10% lower than baseline (negative percent) return percentChange < -REGRESSION_THRESHOLD; } else { // Average time: regression if current is >10% higher than baseline (positive percent) return percentChange > REGRESSION_THRESHOLD; } } } ================================================ FILE: benchmarks/src/main/java/ognl/benchmarks/OgnlPerformanceBenchmarks.java ================================================ package ognl.benchmarks; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.SimpleNode; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @State(Scope.Benchmark) @BenchmarkMode({Mode.AverageTime, Mode.Throughput}) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Fork(value = 1, warmups = 1, jvmArgs = { "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" }) @Warmup(iterations = 3, time = 1) @Measurement(iterations = 5, time = 1, batchSize = 2) public class OgnlPerformanceBenchmarks { private OgnlContext context; private BenchmarkRootBean root; private SimpleNode constantExpression; private SimpleNode compiledConstantExpression; private SimpleNode singlePropertyExpression; private SimpleNode compiledSinglePropertyExpression; private SimpleNode propertyNavigationExpression; private SimpleNode compiledPropertyNavigationExpression; private SimpleNode propertyNavigationAndComparisonExpression; private SimpleNode compiledPropertyNavigationAndComparisonExpression; private SimpleNode propertyNavigationWithMapExpression; private SimpleNode compiledPropertyNavigationWithMapExpression; @Setup public void setup() { try { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); root = new BenchmarkRootBean(); context.put("contextValue", "cvalue"); // Parse and compile expressions constantExpression = (SimpleNode) Ognl.parseExpression("100 + 20 * 5"); compiledConstantExpression = (SimpleNode) Ognl.compileExpression(context, root, "100 + 20 * 5"); singlePropertyExpression = (SimpleNode) Ognl.parseExpression("bean2"); compiledSinglePropertyExpression = (SimpleNode) Ognl.compileExpression(context, root, "bean2"); propertyNavigationExpression = (SimpleNode) Ognl.parseExpression("bean2.bean3.value"); compiledPropertyNavigationExpression = (SimpleNode) Ognl.compileExpression(context, root, "bean2.bean3.value"); propertyNavigationAndComparisonExpression = (SimpleNode) Ognl.parseExpression("bean2.bean3.value <= 24"); compiledPropertyNavigationAndComparisonExpression = (SimpleNode) Ognl.compileExpression(context, root, "bean2.bean3.value <= 24"); propertyNavigationWithMapExpression = (SimpleNode) Ognl.parseExpression("bean2.bean3.map['foo']"); compiledPropertyNavigationWithMapExpression = (SimpleNode) Ognl.compileExpression(context, root, "bean2.bean3.map['foo']"); } catch (Exception e) { throw new RuntimeException("Failed to setup benchmark", e); } } @Benchmark public void constantExpressionInterpreted(Blackhole blackhole) throws OgnlException { Object result = Ognl.getValue(constantExpression, context, root); blackhole.consume(result); } @Benchmark public void constantExpressionCompiled(Blackhole blackhole) { Object result = Ognl.getValue(compiledConstantExpression.getAccessor(), context, root); blackhole.consume(result); } @Benchmark public void singlePropertyExpressionInterpreted(Blackhole blackhole) throws OgnlException { Object result = Ognl.getValue(singlePropertyExpression, context, root); blackhole.consume(result); } @Benchmark public void singlePropertyExpressionCompiled(Blackhole blackhole) { Object result = Ognl.getValue(compiledSinglePropertyExpression.getAccessor(), context, root); blackhole.consume(result); } @Benchmark public void propertyNavigationExpressionInterpreted(Blackhole blackhole) throws OgnlException { Object result = Ognl.getValue(propertyNavigationExpression, context, root); blackhole.consume(result); } @Benchmark public void propertyNavigationExpressionCompiled(Blackhole blackhole) { Object result = Ognl.getValue(compiledPropertyNavigationExpression.getAccessor(), context, root); blackhole.consume(result); } @Benchmark public void propertyNavigationAndComparisonExpressionInterpreted(Blackhole blackhole) throws OgnlException { Object result = Ognl.getValue(propertyNavigationAndComparisonExpression, context, root); blackhole.consume(result); } @Benchmark public void propertyNavigationAndComparisonExpressionCompiled(Blackhole blackhole) { Object result = Ognl.getValue(compiledPropertyNavigationAndComparisonExpression.getAccessor(), context, root); blackhole.consume(result); } @Benchmark public void propertyNavigationWithMapExpressionInterpreted(Blackhole blackhole) throws OgnlException { Object result = Ognl.getValue(propertyNavigationWithMapExpression, context, root); blackhole.consume(result); } @Benchmark public void propertyNavigationWithMapExpressionCompiled(Blackhole blackhole) { Object result = Ognl.getValue(compiledPropertyNavigationWithMapExpression.getAccessor(), context, root); blackhole.consume(result); } // Bean classes for testing public static class BenchmarkRootBean { private BenchmarkNestedBean bean2 = new BenchmarkNestedBean(); public BenchmarkNestedBean getBean2() { return bean2; } } public static class BenchmarkNestedBean { private BenchmarkLeafBean bean3 = new BenchmarkLeafBean(); public BenchmarkLeafBean getBean3() { return bean3; } } public static class BenchmarkLeafBean { private int value = 20; private String nullValue; private int[] indexedValue = new int[100]; private Map map = new HashMap<>(); public BenchmarkLeafBean() { map.put("foo", "bar"); } public int getValue() { return value; } public void setNullValue(String value) { this.nullValue = value; } public int getIndexedValue(int index) { return indexedValue[index]; } public Map getMap() { return map; } } } ================================================ FILE: benchmarks/src/main/java/ognl/benchmarks/OgnlRuntimePerformanceBenchmarks.java ================================================ package ognl.benchmarks; import ognl.OgnlRuntime; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.results.format.ResultFormatType; import java.io.File; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Date; 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; @State(Scope.Benchmark) @BenchmarkMode({Mode.AverageTime, Mode.Throughput}) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Fork(value = 1, warmups = 1, jvmArgs = { "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" }) @Warmup(iterations = 3, time = 1) @Measurement(iterations = 5, time = 1) public class OgnlRuntimePerformanceBenchmarks { private static class Worker implements Callable[]> { private final Class clazz; private final Method method; private final int invocationCount; public Worker(final Class clazz, final Method method, final int invocationCount) { this.clazz = clazz; this.method = method; this.invocationCount = invocationCount; } public Class[] call() { Class[] result = null; for (int i = this.invocationCount; i > 0; i--) { result = OgnlRuntime.findParameterTypes(this.clazz, this.method); } return result; } } private void runTest(final Class clazz, final Method method, final int invocationCount, final int threadCount, final Class[] expected, Blackhole blackhole) throws Exception { final ExecutorService executor = Executors.newFixedThreadPool(threadCount); final List[]>> futures = new ArrayList<>(threadCount); for (int i = threadCount; i > 0; i--) { futures.add(executor.submit(new Worker(clazz, method, invocationCount))); } for (final Future[]> future : futures) { Class[] classes = future.get(); assert expected == classes; blackhole.consume(classes); } executor.shutdown(); } @Benchmark public void testPerformanceRealGenericSingleThread(Blackhole blackhole) throws Exception { final Method barMethod = ExampleStringClass.class.getMethod("bar", Object.class); runTest(ExampleStringClass.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); } @Benchmark public void testPerformanceFakeGenericSingleThread(Blackhole blackhole) throws Exception { final Method fooMethod = ExampleStringClass.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleStringClass.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); } @Benchmark public void testPerformanceNonGenericSingleThread(Blackhole blackhole) throws Exception { final Method fooMethod = ExampleStringSubclass.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleStringSubclass.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); } @Benchmark public void testPerformanceRealGenericMultipleThreads(Blackhole blackhole) throws Exception { final Method barMethod = ExampleStringClass.class.getMethod("bar", Object.class); runTest(ExampleStringClass.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); } @Benchmark public void testPerformanceFakeGenericMultipleThreads(Blackhole blackhole) throws Exception { final Method fooMethod = ExampleStringClass.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleStringClass.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); } @Benchmark public void testPerformanceNotGenericMultipleThreads(Blackhole blackhole) throws Exception { final Method fooMethod = ExampleStringSubclass.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleStringSubclass.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); } @Benchmark public void testPerformanceMultipleClassesMultipleMethodsSingleThread(Blackhole blackhole) throws Exception { Method barMethod = ExampleTwoMethodClass.class.getMethod("bar", String.class); Method fooMethod = ExampleTwoMethodClass.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass2.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass2.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass2.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass2.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass3.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass3.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass3.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass3.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass4.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass4.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass4.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass4.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass5.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass5.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass5.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass5.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass6.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass6.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass6.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass6.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass7.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass7.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass7.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass7.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass8.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass8.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass8.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass8.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass9.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass9.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass9.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass9.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass10.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass10.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass10.class, barMethod, 10000000, 1, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass10.class, fooMethod, 10000000, 1, new Class[]{Integer.class, Date.class}, blackhole); } @Benchmark public void testPerformanceMultipleClassesMultipleMethodsMultipleThreads(Blackhole blackhole) throws Exception { Method barMethod = ExampleTwoMethodClass.class.getMethod("bar", String.class); Method fooMethod = ExampleTwoMethodClass.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass2.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass2.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass2.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass2.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass3.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass3.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass3.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass3.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass4.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass4.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass4.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass4.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass5.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass5.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass5.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass5.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass6.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass6.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass6.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass6.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass7.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass7.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass7.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass7.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass8.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass8.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass8.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass8.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass9.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass9.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass9.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass9.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); barMethod = ExampleTwoMethodClass10.class.getMethod("bar", String.class); fooMethod = ExampleTwoMethodClass10.class.getMethod("foo", Integer.class, Date.class); runTest(ExampleTwoMethodClass10.class, barMethod, 100000, 100, new Class[]{String.class}, blackhole); runTest(ExampleTwoMethodClass10.class, fooMethod, 100000, 100, new Class[]{Integer.class, Date.class}, blackhole); } // Test classes static class GenericClass { @SuppressWarnings("unused") public void bar(final T parameter) { } } static class ExampleStringClass extends GenericClass { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } } static class ExampleStringSubclass extends ExampleStringClass { } static class ExampleTwoMethodClass { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass2 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass3 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass4 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass5 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass6 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass7 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass8 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass9 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } static class ExampleTwoMethodClass10 { @SuppressWarnings("unused") public void foo(final Integer parameter1, final Date parameter2) { } @SuppressWarnings("unused") public void bar(final String parameter2) { } } } ================================================ FILE: docs/DeveloperGuide.md ================================================ --- author: - Drew Davidson - Łukasz Lenart title: OGNL Developer Guide --- # Introduction OGNL as a language allows for the navigation of Java objects through a concise syntax that allows for specifying, where possible, symmetrically settable and gettable values. The language specifies a syntax that attempts to provide as high a level of abstraction as possible for navigating object graphs; this usually means specifying paths through and to JavaBeans properties, collection indices, etc. rather than directly accessing property getters and setters (collectively know as *accessors*). The normal usage of OGNL is to embed the language inside of other constructs to provide a place for flexible binding of values from one place to another. An example of this is a web application where values need to be bound from a model of some sort to data transfer objects that are operated on by a view. Another example is an XML configuration file wherein values are generated via expressions which are then bound to configured objects. ## Embedding OGNL The `ognl.Ognl` class contains convenience methods for evaluating OGNL expressions. You can do this in two stages, parsing an expression into an internal form and then using that internal form to either set or get the value of a property; or you can do it in a single stage, and get or set a property using the String form of the expression directly. It is more efficient to pre-compile the expression to it's parsed form, however, and this is the recommended usage. OGNL expressions can be evaluated without any external context, or they can be provided with an execution environment that sets up custom extensions to modify the way that expressions are evaluated. The following example illustrates how to encapsulate the parsing of an OGNL expression within an object so that execution will be more efficient. The class then takes an `OgnlContext` and a root object to evaluate against. ```java import ognl.Ognl; import ognl.OgnlContext; public class OgnlExpression { private Object expression; public OgnlExpression(String expressionString) throws OgnlException { expression = Ognl.parseExpression(expressionString); } public Object getExpression() { return expression; } public Object getValue(OgnlContext context, Object rootObject) throws OgnlException { return Ognl.getValue(getExpression(), context, rootObject); } public void setValue(OgnlContext context, Object rootObject, Object value) throws OgnlException { Ognl.setValue(getExpression(), context, rootObject, value); } } ``` ## Extending OGNL OGNL expressions are not evaluated in a static environment, as Java programs are. Expressions are not compiled to bytecode at the expression level based on static class reachability. The same expression can have multiple paths through an object graph depending upon the root object specified and the dynamic results of the navigation. Objects that are delegated to handle all of the access to properties of objects, elements of collections, methods of objects, resolution of class names to classes and converting between types are collectively known as *OGNL extensions*. The following chapters delve more deeply into these extensions and provide a roadmap as to how they are used within OGNL to customize the dynamic runtime environment to suit the needs of the embedding program. ### Property Accessors When navigating an OGNL expression many of the elements that are found are properties. Properties can be many things depending on the object being accessed. Most of the time these property names resolve to JavaBeans properties that conform to the set/get pattern. Other objects (such as `Map`) access properties as keyed values. Regardless of access methodology the OGNL syntax remains the same. Under the hood, however, there are `PropertyAccessor` objects that handle the conversion of property name to an actual access to an objects' properties. ```java public interface PropertyAccessor { Object getProperty( Map context, Object target, Object name ) throws OgnlException; void setProperty( Map context, Object target, Object name, Object value ) throws OgnlException; } ``` You can set a property accessor on a class-by-class basis using OgnlRuntime.setPropertyAccessor(). There are default property accessors for `Object` (which uses JavaBeans patterns to extract properties) and `Map` (which uses the property name as a key). ### Method Accessors Method calls are another area where OGNL needs to do lookups for methods based on dynamic information. The MethodAccessor interface provides a hook into how OGNL calls a method. When a static or instance method is requested the implementor of this interface is called to actually execute the method. ```java public interface MethodAccessor { Object callStaticMethod( Map context, Class targetClass, String methodName, List args ) throws MethodFailedException; Object callMethod( Map context, Object target, String methodName, List args ) throws MethodFailedException; } ``` You can set a method accessor on a class-by-class basis using OgnlRuntime.setMethodAccessor(). The is a default method accessor for `Object` (which simply finds an appropriate method based on method name and argument types and uses reflection to call the method). ### Elements Accessors Since iteration is a built-in function of OGNL and many objects support the idea of iterating over the contents of an object (i.e. the object.{ ... } syntax) OGNL provides a hook into how iteration is done. The `ElementsAccessor` interface defines how iteration is done based on a source object. Simple examples could be a `Collection` elements accessor, which would simply ```java public interface ElementsAccessor { Enumeration getElements( Object target ) throws OgnlException; } ``` You can set a method accessor on a class-by-class basis using OgnlRuntime.setElementsAccessor(). There are default elements accessors for `Object` (which returns an `Enumeration` of itself as the only object), `Map` (which iterates over the values in the `Map`), and Collection (which uses the collection's iterator()). One clever use of `ElementsAccessor`s is the `NumberElementsAccessor` class which allows for generating numeric sequences from 0 to the target value. For example the expression `(100).{ #this }` will generate a list of 100 integers ranged 0..99. ### Class References In the sections on accessing static field and static methods it stated that classes must be full-specified in between the class reference specifier (`@````@`<field|method>`@`). This is not entirely true; the default `ClassResolver` simply looks up the name of the class and assumes that it is fully specified. The `ClassResolver` interface is included in the OGNL context to perform lookup of classes when an expression is evaluated. This makes it possible to specify, for example, a list of imports that are specific to a particular `setValue()` or `getValue()` context in order to look up classes. It also makes class references agreeably short because you don't have to full specify a class name. ```java public interface ClassResolver { Class classForName(Map context, String className) throws ClassNotFoundException; } ``` You can set a class resolver on a context basis using the `Ognl` methods addDefaultContext() and createDefaultContext(). ### Type Conversion When performing set operations on properties or calling methods it is often the case that the values you want to set have a different type from the expected type of the class. OGNL supports a context variable (set by `OgnlRuntime.setTypeConverter(Map context, TypeConverter typeConverter)`) that will allow types to be converted from one to another. The default type converter that is uses is the `ognl.DefaultTypeConverter`, which will convert among numeric types `(Integer`, `Long`, `Short`, `Double`, `Float`, `BigInteger`, `BigDecimal`, and their primitive equivalents), string types (`String`, `Character`) and `Boolean`. Should you need specialized type conversion (one popular example is in Servlets where you have a `String[]` from an `HttpServletRequest.getParameters()` and you want to set values with it in other objects; a custom type converter can be written (most likely subclassing `ognl.DefaultTypeConverter`) to convert `String[]` to whatever is necessary. ```java public interface TypeConverter { Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType); } ``` Note that `ognl.DefaultTypeConverter` is much easier to subclass; it implements `TypeConverter` and calls it's own `convertValue(Map context, Object value, Class toType)` method and already provides the numeric conversions. For example, the above converter (i.e. converting `String[]` to `int[]` for a list of identifier parameters in a request) implemented as a subclass of `ognl.DefaultTypeConverter`: ```java HttpServletRequest request; Map context = Ognl.createDefaultContext(this); /* Create an anonymous inner class to handle special conversion */ Ognl.setTypeConverter(context, new ognl.DefaultTypeConverter() { public Object convertValue(Map context, Object value, Class toType) { Object result = null; if ((toType == int[].class) && (value instanceof String[].class)) { String sa = (String[])value; int[] ia = new int[sa.length]; for (int i = 0; i < sa.length; i++) { Integer cv; cv = (Integer)super.convertValue(context, sa[i], Integer.class); ia[i] = cv.intValue(); } result = ia; } else { result = super.convertValue(context, value, toType); } return result; } }); /* Setting values within this OGNL context will use the above-defined TypeConverter */ Ognl.setValue("identifiers", context, this, request.getParameterValues("identifier")); ``` ### Member Access Normally in Java the only members of a class (fields, methods) that can be accessed are the ones defined with public access. OGNL includes an interface that you can set globally (using `OgnlContext.setMemberAccessManager()`) that allows you to modify the runtime in Java 2 to allow access to private, protected and package protected fields and methods. Included in the OGNL package is the `DefaultMemberAccess` class. It contains a constructor that allows you to selectively lower the protection on any private, protected or package protected members` using the AccessibleObject` interface in Java2. The default class can be subclasses to select different objects for which accessibility is allowed. ```java public interface MemberAccess { public Object setup( Member member ); public void restore( Member member, Object state ); public boolean isAccessible( Member member ); } ``` ### Null Handler When navigating a chain sometimes properties or methods will evaluate to null, causing subsequent properties or method calls to fail with `NullPointerException`s. Most of the time this behaviour is correct (as it is with Java), but sometimes you want to be able to dynamically substitute another object in place of `null`. The `NullHandler` interface allows you to specify on a class-by-class basis how nulls are handled within OGNL expressions. Implementing this interface allows you to intercept when methods return `null` and properties evaluate to `null` and allow you to substitute a new value. Since you are given the source of the method or property a really clever implementor might write the property back to the object so that subsequent invocations do not return or evaluate to `null`. ```java public interface NullHandler { public Object nullMethodResult(Map context, Object target, String methodName, List args); public Object nullPropertyValue(Map context, Object target, Object property); } ``` `NullHandler` implementors are registered with OGNL using the `OgnlRuntime.setNullHandler()` method. ## Generic Context As of OGNL 3.5.0, the `OgnlContext` class and related interfaces support a self-referential generic type parameter. This feature enables type-safe custom context implementations while maintaining proper type information throughout the evaluation chain. ### Overview The generic context feature introduces a type parameter `C extends OgnlContext` to: - `OgnlContext` - The main context class - `MemberAccess` - Controls member accessibility - `ClassResolver` - Resolves class names to classes - `TypeConverter` - Converts between types - `Node` - AST node interface - All related interfaces and implementations This allows developers to create custom context implementations that extend `OgnlContext` while preserving type safety throughout the OGNL evaluation process. ### Basic Usage For most use cases, you don't need to specify the generic type explicitly. OGNL provides convenience methods that work with the default `OgnlContext`: ```java // Create a default context OgnlContext context = Ognl.createDefaultContext(rootObject); // Evaluate an expression Object value = Ognl.getValue("property.name", context, rootObject); ``` ### Creating Custom Context Implementations To create a type-safe custom context that extends `OgnlContext`: ```java public class MyCustomContext extends OgnlContext { private String customProperty; public MyCustomContext(MemberAccess memberAccess) { super(memberAccess); } public String getCustomProperty() { return customProperty; } public void setCustomProperty(String value) { this.customProperty = value; } } ``` ### Custom Resolvers and Converters The generic type parameter allows you to create custom implementations that have access to your custom context: ```java public class MyClassResolver> extends DefaultClassResolver { @Override public Class classForName(String className, C context) throws ClassNotFoundException { // Access custom context properties if needed if (context instanceof MyCustomContext) { MyCustomContext myContext = (MyCustomContext) context; // Use custom context properties } return super.classForName(className, context); } } public class MyTypeConverter> extends DefaultTypeConverter { @Override public Object convertValue(C context, Object value, Class toType) { // Custom type conversion logic with access to context if (toType == MyCustomType.class && context instanceof MyCustomContext) { // Use custom context for conversion } return super.convertValue(context, value, toType); } } ``` ### Using Custom Context with OGNL To use your custom context implementation: ```java // Create custom implementations MyCustomContext context = new MyCustomContext(new DefaultMemberAccess()); context.setCustomProperty("custom value"); // Optionally set custom resolver and converter MyClassResolver classResolver = new MyClassResolver<>(); MyTypeConverter typeConverter = new MyTypeConverter<>(); // Create context with custom components MyCustomContext context = new MyCustomContext( new DefaultMemberAccess(), classResolver, typeConverter ); // Set the root object context.withRoot(rootObject); // Evaluate expressions Object value = Ognl.getValue(expression, context, rootObject); ``` ### Using the Builder Pattern `OgnlContext` provides a builder pattern for creating contexts with custom implementations: ```java // Create a builder with a provider function OgnlContext.Builder builder = new OgnlContext.Builder<>( b -> new MyCustomContext( b.getMemberAccess(), b.getClassResolver(), b.getTypeConverter(), b.getInitialContext() ) ); // Configure the builder MyCustomContext context = builder .withMemberAccess(new DefaultMemberAccess()) .withClassResolver(new MyClassResolver<>()) .withTypeConverter(new MyTypeConverter<>()) .withRoot(rootObject) .withInitialContext(initialValues) .build(); ``` ### Type Safety Benefits The generic context provides several type safety benefits: 1. **Compile-time Type Checking**: Custom resolver and converter methods receive the correct context type 2. **IDE Support**: Better code completion and refactoring support 3. **Type Preservation**: The context type is preserved through the evaluation chain 4. **No Casting Required**: When using custom contexts, type casts are minimized ### Backward Compatibility The generic context feature is fully backward compatible. Existing code continues to work without modification: ```java // All existing code continues to work OgnlContext context = Ognl.createDefaultContext(root); Object value = Ognl.getValue(expression, context, root); ``` When you don't specify a generic type, `OgnlContext` is used as the default, which maintains all existing behavior. ### Example: Custom Security Context Here's a complete example implementing a custom context with enhanced security features: ```java public class SecurityContext extends OgnlContext { private final Set allowedPackages; public SecurityContext(MemberAccess memberAccess, Set allowedPackages) { super(memberAccess); this.allowedPackages = allowedPackages; } public boolean isPackageAllowed(String packageName) { return allowedPackages.contains(packageName); } } public class SecurityClassResolver extends DefaultClassResolver { @Override public Class classForName(String className, SecurityContext context) throws ClassNotFoundException { // Extract package name String packageName = className.substring(0, className.lastIndexOf('.')); // Check if package is allowed if (!context.isPackageAllowed(packageName)) { throw new ClassNotFoundException( "Access to package " + packageName + " is not allowed"); } return super.classForName(className, context); } } // Usage Set allowedPackages = Set.of("com.myapp", "java.util"); SecurityContext context = new SecurityContext( new DefaultMemberAccess(), allowedPackages ); context.setClassResolver(new SecurityClassResolver()); context.withRoot(rootObject); // Only classes from allowed packages can be accessed Object value = Ognl.getValue("@com.myapp.MyClass@getValue()", context, rootObject); ``` ## Other API features ### Tracing Evaluations As of OGNL 2.5.0 the `OgnlContext` object can automatically tracks evaluations of expressions. This tracking is kept in the `OgnlContext` as currentEvaluation during the evaluation. After execution you can access the last evaluation through the lastEvaluation property of `OgnlContext`. > **Note** > > The tracing feature is turned off by default. If you wish to turn it > on there is a `setTraceEvaluations()` method on `OgnlContext` that you > can call. Any [method accessor](#method-accessors), [elements accessor](#elements-accessors), [type converter](#type-conversion), [property accessor](#property-accessors) or [null handler](#null-handler) may find this useful to give context to the operation being performed. The `Evaluation` object is itself a tree and can be traversed up, down and left and right through siblings to determine the exact circumstances of an evaluation. In addition the `Evaluation` object tracks the node that was performing the operation, the source object on which that operation was being performed and the result of the operation. If an exception is thrown during execution the user can get the last evaluation's last descendant to find out exactly which subexpression caused the error. The exception is also tracked in the `Evaluation`. ================================================ FILE: docs/ISSUE_103_ANALYSIS.md ================================================ # Issue #103: Class Reference Parser Fails with "or" in Package Names ## Problem Summary The OGNL parser fails to parse class references containing reserved keywords (like "or", "and", "not", "in", etc.) within package names. This is a **parsing-time error**, not a runtime error. ### Example Failing Expression ```java @jp.or.example.IdUtils@generateId() ``` ### Error Message ``` ognl.ExpressionSyntaxException: Malformed OGNL expression Encountered "or" "or" at line 1, column 5. Was expecting: ``` ### Root Cause In JavaCC (the parser generator used by OGNL), when string literals like "or", "and", "not" are used in grammar productions as operators, they become implicit **keyword tokens** that take precedence over the generic `` token during lexical analysis. The `className()` production in `ognl.jj` (lines 1374-1382) expects only `` tokens: ```java String className(): { Token t; StringBuffer result; } { t= { result = new StringBuffer( t.image ); } ( "." t= { result.append('.').append( t.image ); } )* { return new String(result); } } ``` When parsing `@jp.or.example@`, the lexer encounters "or" and tokenizes it as the keyword "or" (used for logical OR expressions on line 179) rather than as an identifier, causing a parse error. ## Solution The fix introduces a new helper production `classNamePart()` that accepts **either** an `` token **or** any of the reserved keywords, treating them as identifiers in the context of class/package names. ### Grammar Changes #### 1. New Helper Production (`ognl.jj:1389-1418`) ```java /** * Helper production to match class name parts, which can be either identifiers * or reserved keywords (like "or", "and", "not", etc.) that appear in package names. * This fixes Issue #103 where package names containing keywords would fail to parse. */ Token classNamePart(): { Token t; } { ( t= | "or" { t = token; } | "and" { t = token; } | "not" { t = token; } | "in" { t = token; } | "bor" { t = token; } | "xor" { t = token; } | "band" { t = token; } | "eq" { t = token; } | "neq" { t = token; } | "lt" { t = token; } | "lte" { t = token; } | "gt" { t = token; } | "gte" { t = token; } | "shl" { t = token; } | "shr" { t = token; } | "ushr" { t = token; } | "new" { t = token; } | "true" { t = token; } | "false" { t = token; } | "null" { t = token; } | "instanceof" { t = token; } ) { return t; } } ``` #### 2. Updated `className()` Production (`ognl.jj:1374-1382`) ```java String className(): { Token t; StringBuffer result; } { t=classNamePart() { result = new StringBuffer( t.image ); } ( "." t=classNamePart() { result.append('.').append( t.image ); } )* { return new String(result); } } ``` #### 3. Updated `instanceof` Production (`ognl.jj:948-969`) The instanceof expression also constructs class names with dots, so it was updated to use `classNamePart()` instead of ``: ```java "instanceof" t = classNamePart() ... ( "." t = classNamePart() { sb.append('.').append( t.image ); } )* ``` ## Test Coverage A new test class `PackageKeywordTest.java` was created with comprehensive test cases: 1. **Baseline Test**: Verifies that normal Java classes work (`java.util.UUID`) 2. **Keyword Tests**: Tests parsing expressions with keywords in package names: - `@jp.or.example.IdUtils@generateId()` - "or" keyword - `@com.and.example.Utils@method()` - "and" keyword - `@org.not.example.Utils@method()` - "not" keyword - `@org.example.in.Utils@method()` - "in" keyword - `@org.not.and.or.Utils@field` - Multiple keywords The tests verify that these expressions **parse successfully** without throwing `ExpressionSyntaxException`. While the classes may not exist at runtime (causing different exceptions), the key is that parsing completes successfully. ## Impact Analysis ### Backward Compatibility ✅ **FULLY BACKWARD COMPATIBLE** - The fix only **expands** the set of valid expressions; it doesn't change the parsing of any previously valid expressions - All existing tests should continue to pass - No API changes to public classes ### Why This is Safe 1. **No ambiguity introduced**: The context is unambiguous - we're parsing between `@` symbols where only class names are expected 2. **Keywords remain keywords in expressions**: The keywords still function as operators in expression contexts (e.g., `a or b` still works) 3. **Natural behavior**: This aligns with Java's own grammar where keywords can appear in qualified class names in certain contexts 4. **Real-world need**: Japanese domain names (`.jp.or.jp`) commonly use "or" in their structure ### Affected Components **Modified Files:** - `ognl/src/main/javacc/ognl.jj` - Grammar definition - `ognl/target/generated-sources/java/ognl/OgnlParser.java` - Auto-generated (after compile) - `ognl/target/generated-sources/java/ognl/OgnlParserTokenManager.java` - Auto-generated (after compile) **New Files:** - `ognl/src/test/java/ognl/test/PackageKeywordTest.java` - Test coverage ## Build Instructions After modifying the grammar file, the parser must be regenerated: ```bash # From the ognl module directory mvn compile # This will: # 1. Run javacc-maven-plugin to regenerate parser from ognl.jj # 2. Compile the generated parser classes # 3. Compile the main OGNL sources ``` ## Verification Steps 1. **Compile the project**: `mvn clean compile` 2. **Run the new test**: `mvn test -Dtest=PackageKeywordTest` 3. **Run full test suite**: `mvn test` (all 607 tests should pass) 4. **Verify the expressions work**: ```java // Test that previously failing expressions now parse Ognl.parseExpression("@jp.or.example.Class@method()"); Ognl.parseExpression("@org.not.and.or.Utils@field"); ``` ## Similar Issues in Other Projects This type of issue is common in parser-based expression languages: 1. **Spring Expression Language (SpEL)**: Similar issues with T() operator 2. **ANTLR grammars**: Often need special handling for keywords in dotted names 3. **JavaScript**: "yield" and "await" had similar problems in property names The standard solution (which we've applied) is to explicitly allow keywords in contexts where they cannot be confused with their operator usage. ## Alternative Solutions Considered 1. ❌ **Quote the keyword parts**: `@jp."or".example@` - Too verbose and breaks compatibility with other expression languages 2. ❌ **Escape sequences**: `@jp.\\or.example@` - Confusing and non-intuitive 3. ❌ **Change tokenization rules**: Would affect the entire grammar, potentially breaking existing expressions 4. ✅ **Context-specific keyword handling**: Our chosen solution - surgical fix with no side effects ## Conclusion This fix resolves Issue #103 by allowing OGNL reserved keywords to appear as part of fully-qualified class names, which is essential for supporting real-world Java package structures (particularly Japanese domain-based packages like `jp.or.*`). The solution is: - ✅ Minimal and surgical - ✅ Fully backward compatible - ✅ Well-tested - ✅ Follows established patterns in parser design - ✅ Aligns with Java's own behavior ## Next Steps 1. Verify the fix compiles and regenerates the parser correctly 2. Run the full test suite to ensure no regressions 3. Test with real-world Japanese domain packages if available 4. Commit the changes 5. Create a pull request with this analysis --- **Files Modified:** - `ognl/src/main/javacc/ognl.jj` - `ognl/src/test/java/ognl/test/PackageKeywordTest.java` (new) **Generated Files (after build):** - `ognl/target/generated-sources/java/ognl/OgnlParser.java` - `ognl/target/generated-sources/java/ognl/OgnlParserTokenManager.java` ================================================ FILE: docs/LanguageGuide.md ================================================ --- author: - Drew Davidson title: OGNL Language Guide --- # Introduction OGNL stands for **O**bject **G**raph **N**avigation **L**anguage. It is an expression and binding language for getting and setting properties of Java objects. Normally the same expression is used for both getting and setting the value of a property. We pronounce OGNL as a word, like the last syllables of a drunken pronunciation of "orthogonal." Many people have asked exactly what OGNL is good for. Several of the uses to which OGNL has been applied are: - A binding language between GUI elements (textfield, combobox, etc.) to model objects. Transformations are made easier by OGNL's TypeConverter mechanism to convert values from one type to another (String to numeric types, for example). - A data source language to map between table columns and a Swing TableModel. - A binding language between web components and the underlying model objects ([WebOGNL](http://www.ognl.org), [Tapestry](http://jakarta.apache.org/tapestry/index.html), [WebWork](http://sourceforge.net/projects/opensymphony), [WebObjects](http://wonder.sourceforge.net/index.html)). - A more expressive replacement for the property-getting language used by the Jakarta Commons BeanUtils package or JSTL's EL (which only allow simple property navigation and rudimentary indexed properties). Most of what you can do in Java is possible in OGNL, plus other extras such as list [projection](#projecting-across-collections) and [selection](#selecting-from-collections) and [lambda expressions](#pseudo-lambda-expressions). ## History OGNL started out as a way to set up associations between UI components and controllers using property names. As the desire for more complicated associations grew, Drew Davidson created what he called KVCL, for Key-Value Coding Language, egged on by Luke Blanshard. Luke then reimplemented the language using ANTLR, came up with the new name, and, egged on by Drew, filled it out to its current state. Later on Luke again reimplemented the language using [JavaCC](http://www.webgain.com/products/java_cc/). Further maintenance on all the code is done by Drew (with spiritual guidance from Luke). ## Syntax Simple OGNL expressions are very simple. The language has become quite rich with features, but you don't generally need to worry about the more complicated parts of the language: the simple cases have remained that way. For example, to get at the name property of an object, the OGNL expression is simply name. To get at the text property of the object returned by the headline property, the OGNL expression is headline.text. What is a property? Roughly, an OGNL property is the same as a bean property, which means that a pair of get/set methods, or alternatively a field, defines a property (the full story is a bit more complicated, since properties differ for different kinds of objects; see below for a full explanation). The fundamental unit of an OGNL expression is the navigation chain, usually just called "chain." The simplest chains consist of the following parts: |Expression Element Part|Example| |-----------------------|----------------------------------------------------------------| |Property names|like the name and headline.text examples above| |Method Calls|hashCode() to return the current object's hash code| |Array Indices|listeners[0] to return the first of the current object's list of listeners| All OGNL expressions are evaluated in the context of a current object, and a chain simply uses the result of the previous link in the chain as the current object for the next one. You can extend a chain as long as you like. For example, this chain: ``` name.toCharArray()[0].numericValue.toString() ``` This expression follows these steps to evaluate: - extracts the name property of the initial, or root, object (which the user provides to OGNL through the OGNL context) - calls the toCharArray() method on the resulting `String` - extracts the first character (the one at index 0) from the resulting array - gets the numericValue property from that character (the character is represented as a `Character` object, and the `Character` class has a method called getNumericValue()). - calls toString() on the resulting `Integer` object. The final result of this expression is the `String` returned by the last toString() call. Note that this example can only be used to get a value from an object, not to set a value. Passing the above expression to the Ognl.setValue() method would cause an `InappropriateExpressionException` to be thrown, because the last link in the chain is neither a property name nor an array index. This is enough syntax to do the vast majority of what you ever need to do. ## Expressions This section outlines the details the elements of OGNL's expressions. ## Constants OGNL has the following kinds of constants: - String literals, as in Java (with the addition of single quotes): delimited by single- or double-quotes, with the full set of character escapes. - Character literals, also as in Java: delimited by single-quotes, also with the full set of escapes. - Numeric literals, with a few more kinds than Java. In addition to Java's ints, longs, floats and doubles, OGNL lets you specify BigDecimals with a "b" or "B" suffix, and BigIntegers with an "h" or "H" suffix (think "huge"---we chose "h" for BigIntegers because it does not interfere with hexadecimal digits). - Boolean `(true` and `false`) literals. - The `null` literal. ## Referring to Properties OGNL treats different kinds of objects differently in its handling of property references. Maps treat all property references as element lookups or storage, with the property name as the key. Lists and arrays treat numeric properties similarly, with the property name as the index, but string properties the same way ordinary objects do. Ordinary objects (that is, all other kinds) only can handle string properties and do so by using "get" and "set" methods (or "is" and "set"), if the object has them, or a field with the given name otherwise. Note the new terminology here. Property "names" can be of any type, not just Strings. But to refer to non-String properties, you must use what we have been calling the "index" notation. For example, to get the length of an array, you can use this expression: array.length But to get at element 0 of the array, you must use an expression like this: array[0] Note that Java collections have some special properties associated with them. See [Pseudo-Properties for Collections](#pseudo-properties-for-collections) for these properties. ## Indexing As discussed above, the "indexing" notation is actually just property reference, though a computed form of property reference rather than a constant one. For example, OGNL internally treats the "array.length" expression exactly the same as this expression: array["length"] And this expression would have the same result (though not the same internal form): array["len" + "gth"] ### Array and List Indexing For Java arrays and Lists indexing is fairly simple, just like in Java. An integer index is given and that element is the referrent. If the index is out of bounds of the array or List and IndexOutOfBoundsException is thrown, just as in Java. ### JavaBeans Indexed Properties JavaBeans supports the concept of Indexed properties. Specifically this means that an object has a set of methods that follow the following pattern: - public PropertyType[] getPropertyName() - public void setPropertyName(PropertyType[] anArray) - public PropertyType getPropertyName(int index) - public void setPropertyName(int index, PropertyType value) OGNL can interpret this and provide seamless access to the property through the indexing notation. References such as someProperty[2] are automatically routed through the correct indexed property accessor (in the above case through `getSomeProperty(2)` or `setSomeProperty(2, value)`). If there is no indexed property accessor a property is found with the name `someProperty` and the index is applied to that. ### OGNL Object Indexed Properties OGNL extends the concept of indexed properties to include indexing with arbitrary objects, not just integers as with JavaBeans Indexed Properties. When finding properties as candidates for object indexing, OGNL looks for patterns of methods with the following signature: - public PropertyType getPropertyName(IndexType index) - public void setPropertyName(IndexType index, PropertyType value) The PropertyType and IndexType must match each other in the corresponding set and get methods. An actual example of using Object Indexed Properties is with the Servlet API: the Session object has two methods for getting and setting arbitrary attributes: public Object getAttribute(String name) public void setAttribute(String name, Object value) An OGNL expression that can both get and set one of these attributes is session.attribute["foo"] ## Calling Methods OGNL calls methods a little differently from the way Java does, because OGNL is interpreted and must choose the right method at run time, with no extra type information aside from the actual arguments supplied. OGNL always chooses the most specific method it can find whose types match the supplied arguments; if there are two or more methods that are equally specific and match the given arguments, one of them will be chosen arbitrarily. In particular, a null argument matches all non-primitive types, and so is most likely to result in an unexpected method being called. Note that the arguments to a method are separated by commas, and so the comma operator cannot be used unless it is enclosed in parentheses. For example, method( ensureLoaded(), name ) is a call to a 2-argument method, while method( (ensureLoaded(), name) ) is a call to a 1-argument method. ## Variable References OGNL has a simple variable scheme, which lets you store intermediate results and use them again, or just name things to make an expression easier to understand. All variables in OGNL are global to the entire expression. You refer to a variable using a number sign in front of its name, like this: #var OGNL also stores the current object at every point in the evaluation of an expression in the this variable, where it can be referred to like any other variable. For example, the following expression operates on the number of listeners, returning twice the number if it is more than 100, or 20 more than the number otherwise: listeners.size().(#this > 100? 2*#this : 20+#this) OGNL can be invoked with a map that defines initial values for variables. The standard way of invoking OGNL defines the variables `root` (which holds the initial, or root, object), and `context` (which holds the `Map` of variables itself). To assign a value to a variable explicitly, simply write an assignment statement with a variable reference on the left-hand side: #var = 99 ## Parenthetical Expressions As you would expect, an expression enclosed in parentheses is evaluated as a unit, separately from any surrounding operators. This can be used to force an evaluation order different from the one that would be implied by OGNL operator precedences. It is also the only way to use the comma operator in a method argument. ## Chained Subexpressions If you use a parenthetical expression after a dot, the object that is current at the dot is used as the current object throughout the parenthetical expression. For example, headline.parent.(ensureLoaded(), name) traverses through the headline and parent properties, ensures that the parent is loaded and then returns (or sets) the parent's name. Top-level expressions can also be chained in this way. The result of the expression is the right-most expression element. ensureLoaded(), name This will call `ensureLoaded()` on the root object, then get the name property of the root object as the result of the expression. ### Limitations OGNL won't be able to process Java packages which match any of the token names, eg.: `or.mypackage.MyClass@someMethod` or `com.mypackage.or.MyClass@sometMethod` - in such cases `or` will be treated as an operator. See the list of operators below to understand possible implications. ## Collection Construction ### Lists To create a list of objects, enclose a list of expressions in curly braces. As with method arguments, these expressions cannot use the comma operator unless it is enclosed in parentheses. Here is an example: name in { null,"Untitled" } This tests whether the `name` property is `null` or equal to `"Untitled"`. The syntax described above will create a instanceof the `List` interface. The exact subclass is not defined. ### Native Arrays Sometimes you want to create Java native arrays, such as `int[]` or `Integer[]`. OGNL supports the creation of these similarly to the way that constructors are normally called, but allows initialization of the native array from either an existing list or a given size of the array. new int[] { 1, 2, 3 } This creates a new `int` array consisting of three integers 1, 2 and 3. To create an array with all `null` or `0` elements, use the alternative size constructor new int[5] This creates an `int` array with 5 slots, all initialized to zero. ### Maps Maps can also be created using a special syntax. #{ "foo" : "foo value", "bar" : "bar value" } This creates a Map initialized with mappings for `"foo"` and `"bar"`. Advanced users who wish to select the specific Map class can specify that class before the opening curly brace #@java.util.LinkedHashMap@{ "foo" : "foo value", "bar" : "bar value" } The above example will create an instance of the JDK 1.4 class `LinkedHashMap`, ensuring the the insertion order of the elements is preserved. ## Projecting Across Collections OGNL provides a simple way to call the same method or extract the same property from each element in a collection and store the results in a new collection. We call this "projection," from the database term for choosing a subset of columns from a table. For example, this expression: listeners.{delegate} returns a list of all the listeners' delegates. See the coercion section for how OGNL treats various kinds of objects as collections. During a projection the `#this` variable refers to the current element of the iteration. objects.{ #this instanceof String ? #this : #this.toString()} The above would produce a new list of elements from the objects list as string values. ## Selecting From Collections OGNL provides a simple way to use an expression to choose some elements from a collection and save the results in a new collection. We call this "selection," from the database term for choosing a subset of rows from a table. For example, this expression: listeners.{? #this instanceof ActionListener} returns a list of all those listeners that are instances of the `ActionListener` class. See the [operators](#operators) for how OGNL treats various kinds of objects as collections. ### Selecting First Match In order to get the first match from a list of matches, you could use indexing such as `listeners.{? true }[0]`. However, this is cumbersome because if the match does not return any results (or if the result list is empty) you will get an `ArrayIndexOutOfBoundsException`. The selection syntax is also available to select only the first match and return it as a list. If the match does not succeed for any elements an empty list is the result. objects.{^ #this instanceof String } Will return the first element contained in objects that is an instance of the `String` class. ### Selecting Last Match Similar to getting the first match, sometimes you want to get the last element that matched. objects.{$ #this instanceof String } This will return the last element contained in objects that is an instanceof the `String` class ## Calling Constructors You can create new objects as in Java, with the `new` operator. One difference is that you must specify the fully qualified class name for classes other than those in the java.lang package.[^1] (for example, `new java.util.ArrayList()`, rather than simply `new ArrayList()`). OGNL chooses the right constructor to call using the same procedure it uses for overloaded method calls. ## Calling Static Methods You can call a static method using the syntax `@``class``@``method(args)`. If you leave out class, it defaults to `java.lang.Math`, to make it easier to call `min` and `max` methods. If you specify the class, you must give the fully qualified name. If you have an instance of a class whose static method you wish to call, you can call the method through the object as if it was an instance method. If the method name is overloaded, OGNL chooses the right static method to call using the same procedure it uses for overloaded instance methods. ## Getting Static Fields You can refer to a static field using the syntax `@``class``@``field`. The class must be fully qualified. ## Expression Evaluation If you follow an OGNL expression with a parenthesized expression, without a dot in front of the parentheses, OGNL will try to treat the result of the first expression as another expression to evaluate, and will use the result of the parenthesized expression as the root object for that evaluation. The result of the first expression may be any object; if it is an AST, OGNL assumes it is the parsed form of an expression and simply interprets it; otherwise, OGNL takes the string value of the object and parses that string to get the AST to interpret. For example, this expression #fact(30H) looks up the fact variable, and interprets the value of that variable as an OGNL expression using the `BigInteger` representation of `30` as the root object. See below for an example of setting the `fact` variable with an expression that returns the factorial of its argument. Note that there is an ambiguity in OGNL's syntax between this double evaluation operator and a method call. OGNL resolves this ambiguity by calling anything that looks like a method call, a method call. For example, if the current object had a fact property that held an OGNL factorial expression, you could not use this approach to call it fact(30H) because OGNL would interpret this as a call to the fact method. You could force the interpretation you want by surrounding the property reference by parentheses: (fact)(30H) ## Pseudo-Lambda Expressions OGNL has a simplified lambda-expression syntax, which lets you write simple functions. It is not a full-blown lambda calculus, because there are no closures---all variables in OGNL have global scope and extent. For example, here is an OGNL expression that declares a recursive factorial function, and then calls it: #fact = :[#this<=1? 1 : #this*#fact(#this-1)], #fact(30H) The lambda expression is everything inside the brackets. The \#this variable holds the argument to the expression, which is initially `30H`, and is then one less for each successive call to the expression. OGNL treats lambda expressions as constants. The value of a lambda expression is the AST that OGNL uses as the parsed form of the contained expression. ## Pseudo-Properties for Collections There are some special properties of collections that OGNL makes available. The reason for this is that the collections do not follow JavaBeans patterns for method naming; therefore the `size()`, `length()`, etc. methods must be called instead of more intuitively referring to these as properties. OGNL corrects this by exposing certain pseudo-properties as if they were built-in. |Collection |Special Properties | |-------------------------------------------------|--------------------------------------------------------------------| |`Collection` (inherited by `Map`, `List` & `Set`)|`size` - The size of the collection| | |`isEmpty` - Evaluates to `true` if the collection is empty| |`List` |`iterator` - Evaluates to an `Iterator` over the `List`.| |`Map` |`keys` - Evaluates to a `Set` of all keys in the `Map`.| | |`values` - Evaluates to a `Collection` of all values in the `Map`.| | |> **Note** These properties, plus `size` and `isEmpty`, are different than the indexed form of access for `Map`s (i.e. `someMap["size"]` gets the `"size"` key from the map, whereas `someMap.size` gets the size of the `Map`.| |`Set` |`iterator` - Evaluates to an `Iterator` over the `Set`.| |`Iterator` |`next` - Evaluates to the next object from the `Iterator`.| | |`hasNext` - Evaluates to `true` if there is a next object available from the `Iterator`.| |`Enumeration` |`next` - Evaluates to the next object from the `Enumeration`.| | |`hasNext` - Evaluates to `true` if there is a next object available from the `Enumeration`.| | |`nextElement` - Synonym for `next`.| | |`hasMoreElements` - Synonym for `hasNext`.| ## Operators that differ from Java's operators For the most part, OGNL's operators are borrowed from Java and work similarly to Java's operators. See the OGNL Reference for a complete discussion. Here we describe OGNL operators that are not in Java, or that are different from Java. - The comma (,) or sequence operator. This operator is borrowed from C. The comma is used to separate two independent expressions. The value of the second of these expressions is the value of the comma expression. Here is an example: ``` ensureLoaded(), name ``` When this expression is evaluated, the ensureLoaded method is called (presumably to make sure that all parts of the object are in memory), then the name property is retrieved (if getting the value) or replaced (if setting). - List construction with curly braces ({}). You can create a list in-line by enclosing the values in curly braces, as in this example: ``` { null, true, false } ``` - The `in` operator (and `not in`, its negation). This is a containment test, to see if a value is in a collection. For example, ``` name in {null,"Untitled"} || name ``` - The null-safe navigation operator (`?.`). This operator allows safe navigation through object graphs without throwing exceptions when encountering null values. When you use `?.` instead of `.`, the expression returns null if the object is null, rather than throwing an exception. For example: ``` user?.profile?.address?.city ``` If `user`, `profile`, or `address` is null, the entire expression returns null instead of throwing a NullPointerException. This is particularly useful for deeply nested property access where intermediate values might be null. Each `?.` operator is independent - you can mix null-safe (`?.`) and regular (`.`) navigation in the same expression: ``` user.profile?.address.city ``` In this example, only the `address` access is null-safe. The null-safe operator works with property access, method calls, and collection operations. See `docs/NullSafeOperator.md` for complete details. - See the OGNL reference for a full list of operations ## Setting values versus getting values As stated before, some values that are gettable are not also settable because of the nature of the expression. For example, names[0].location is a settable expression - the final component of the expression resolves to a settable property. However, some expressions, such as names[0].length + 1 are not settable because they do not resolve to a settable property in an object. It is simply a computed value. If you try to evaluate this expression using any of the `Ognl.setValue()` methods it will fail with an `InappropriateExpressionException`. It is also possible to set variables using get expressions that include the '`=`' operator. This is useful when a get expression needs to set a variable as a side effect of execution. ## Coercing Objects to Types Here we describe how OGNL interprets objects as various types. See below for how OGNL coerces objects to booleans, numbers, integers, and collections. ## Interpreting Objects as Booleans Any object can be used where a boolean is required. OGNL interprets objects as booleans like this: - If the object is a `Boolean`, its value is extracted and returned - If the object is a `Number`, its double-precision floating-point value is compared with zero; non-zero is treated as `true`, zero as `false`. - If the object is a `Character`, its boolean value is `true` if and only if its char value is non-zero. - Otherwise, its boolean value is `true` if and only if it is non-`null`. ## Interpreting Objects as Numbers Numerical operators try to treat their arguments as numbers. The basic primitive-type wrapper classes (Integer, Double, and so on, including Character and Boolean, which are treated as integers), and the "big" numeric classes from the java.math package (BigInteger and BigDecimal), are recognized as special numeric types. Given an object of some other class, OGNL tries to parse the object's string value as a number. Numerical operators that take two arguments use the following algorithm to decide what type the result should be. The type of the actual result may be wider, if the result does not fit in the given type. - If both arguments are of the same type, the result will be of the same type if possible. - If either argument is not of a recognized numeric class, it will be treated as if it was a `Double` for the rest of this algorithm. - If both arguments are approximations to real numbers `(Float`, `Double`, or `BigDecimal`), the result will be the wider type. - If both arguments are integers `(Boolean`, `Byte`, `Character`, `Short`, `Integer`, `Long`, or `BigInteger`), the result will be the wider type. - If one argument is a real type and the other an integer type, the result will be the real type if the integer is narrower than "int"; `BigDecimal` if the integer is `BigInteger`; or the wider of the real type and `Double` otherwise. ## Interpreting Objects as Integers Operators that work only on integers, like the bit-shifting operators, treat their arguments as numbers, except that `BigDecimal`s and `BigInteger`s are operated on as `BigInteger`s and all other kinds of numbers are operated on as Longs. For the `BigInteger` case, the result of these operators remains a `BigInteger`; for the `Long` case, the result is expressed as the same type of the arguments, if it fits, or as a `Long` otherwise. ## Interpreting Objects as Collections The projection and selection operators (`e1.{e2}` and `e1.{?e2}`), and the `in` operator, all treat one of their arguments as a collection and walk it. This is done differently depending on the class of the argument: - Java arrays are walked from front to back - Members of `java.util.Collection` are walked by walking their iterators - Members of `java.util.Map` are walked by walking iterators over their values - Members of `java.util.Iterator` and `java.util.Enumeration` are walked by iterating them - Members of `java.lang.Number` are "walked" by returning integers less than the given number starting with zero - All other objects are treated as singleton collections containing only themselves ## OGNL Language Reference This section has a fairly detailed treatment of OGNL's syntax and implementation. See below for a complete table of OGNL's operators, a section on how OGNL coerces objects to various types, and a detailed description of OGNL's basic expressions. ### Operators OGNL borrows most of Java's operators, and adds a few new ones. For the most part, OGNL's treatment of a given operator is the same as Java's, with the important caveat that OGNL is essentially a typeless language. What that means is that every value in OGNL is a Java object, and OGNL attempts to coerce from each object a meaning appropriate to the situation it is used in. The following table lists OGNL operators in reverse precedence order. When more than one operator is listed in the same box, these operators have the same precedence and are evaluated in left-to-right order. |Operator |`getValue()` Notes |`setValue()` Notes| |----------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------| |e1`,`e2 - Sequence operator |Both `e1` and `e2` are evaluated with the same source object, and the result of `e2` is returned. |`getValue` is called on `e1`, and then `setValue` is called on `e2`.| |e1 `=` e2 - Assignment operator |`getValue` is called on `e2`, and then `setValue` is called on `e1` with the result of `e2` as the target object. |Cannot be the top-level expression for `setValue`.| |e1 `?` e2 `:` e3 - Conditional operator |`getValue` is called on `e1` and the result is [interpreted as a boolean](#interpreting-objects-as-booleans). `getValue` is then called on either `e2` or `e3`, depending on whether the result of `e1` was `true` or `false` respectively, and the result is returned. |`getValue` is called on `e1`, and then `setValue` is called on either `e2` or `e3`.| |e1 `\|\|` e2; e1 `or` e2 - Logical or operator |`getValue` is called on `e1` and the result is [interpreted as a boolean](#interpreting-objects-as-booleans). If `true`, that result is returned; if `false`, `getValue` is called on `e2` and its value is returned. |`getValue` is called on `e1`; if `false`, `setValue` is called on `e2`. Note that `e1` being `true` prevents any further setting from taking place.| |e1 `&&` e2; e1 `and` e2 - Logical and operator |`getValue` is called on `e1` and the result is [interpreted as a boolean](#interpreting-objects-as-booleans). If `false`, that result is returned; if true, `getValue` is called on e2 and its value is returned. |`getValue` is called on `e1`; if `true`, `setValue` is called on `e2`. Note that `e1` being `false` prevents any further setting from taking place.| |e1 `\|` e2; e1 `bor` e2 - Bitwise or operator |`e1` and `e2` are [interpreted as integers](#interpreting-objects-as-integers) and the result is an `integer`. |Cannot be the top-level expression passed to `setValue`.| |e1 `^` e2; e1 `xor` e2 - Bitwise exclusive-or operator |`e1` and `e2` are [interpreted as integers](#interpreting-objects-as-integers) and the result is an `integer`. |Cannot be the top-level expression passed to `setValue`.| |e1 `&` e2; e1 `band` e2 - Bitwise and operator |`e1` and `e2` are [interpreted as integers](#interpreting-objects-as-integers) and the result is an `integer`. |Cannot be the top-level expression passed to `setValue`.| |e1 `==` e2; e1 `eq` e2 - Equality test |Equality is tested for as follows. If either value is `null`, they are equal if and only if both are `null`. If they are the same object or the `equals()` method says they are equal, they are equal. If they are both `Number`s, they are equal if their values as double-precision floating point numbers are equal. Otherwise, they are not equal. These rules make numbers compare equal more readily than they would normally, if just using the equals method. |Cannot be the top-level expression passed to `setValue`.| |e1 `!=` e2; e1 `neq` e2 - Inequality test | | | |e1 `<` e2; e1 `lt` e2 - Less than comparison |The ordering operators compare with `compareTo()` if their arguments are non-numeric and implement `Comparable`; otherwise, the arguments are interpreted as numbers and compared numerically. The in operator is not from Java; it tests for inclusion of e1 in e2, where e2 is interpreted as a collection. This test is not efficient: it iterates the collection. However, it uses the standard OGNL equality test. |Cannot be the top-level expression passed to `setValue`.| |e1 `<=` e2; e1 `lte` e2 - Less than or equals comparison | | | |e1 `> `e2; e1 `gt` e2 - Greater than comparison | | | |e1 `>=` e2; e1 `gte` e2 - Greater than or equals comparison | | | |e1` in` e2 - List membership comparison | | | |e1 `not in` e2 - List non-membership comparison | | | |e1 `<<` e2; e1 `shl` e2 - Bit shift left |`e1` and `e2` are [interpreted as integers](#interpreting-objects-as-integers) and the result is an `integer`. |Cannot be the top-level expression passed to `setValue`.| |e1 `>>` e2; e1 `shr` e2 - Bit shift right | | | |e1 `>>>` e2; e1 `ushr` e2 - Logical shift right | | | |e1 `+` e2 - Addition |The plus operator concatenates strings if its arguments are non-numeric; otherwise it [interprets its arguments as numbers](#interpreting-objects-as-numbers) and adds them. The minus operator always works on numbers. |Cannot be the top-level expression passed to `setValue`.| |e1 `-` e2 - Subtraction | | | |e1 `*` e2 - Multiplication |Multiplication, division, which [interpret their arguments as numbers](#interpreting-objects-as-numbers), and remainder, which [interprets its arguments as integers](#interpreting-objects-as-integers). |Cannot be the top-level expression passed to `setValue`.| |e1 `/` e2 - Division | | | |e1 `%` e2 - Remainder | | | |`+ `e - Unary plus |Unary plus is a no-op, it simply returns the value of its argument. Unary minus [interprets its argument as a number](#interpreting-objects-as-numbers). Logical not [interprets its argument as a boolean](#interpreting-objects-as-booleans). Bitwise not [interprets its argument as an integer](#interpreting-objects-as-integers). The class argument to instanceof is the fully qualified name of a Java class. |Cannot be the top-level expression passed to `setValue`.| |`-` e - Unary minus | | | |`!` e; `not` e - Logical not | | | |`~` e - Bitwise not | | | |e `instanceof` class - Class membership | | | |[e`.`method`(`args`)`](#calling-methods) - Method call |Generally speaking, navigation chains are evaluated by evaluating the first expression, then evaluating the second one with the result of the first as the source object. |Some of these forms can be passed as top-level expressions to `setValue` and others cannot. Only those chains that end in property references (e.property), indexes (`e1[e2]`), and subexpressions (`e1.(e2)`) can be; and expression evaluations can be as well. For the chains, `getValue` is called on the left-hand expression (`e` or `e1`), and then `setValue` is called on the rest with the result as the target object.| |[e`.`property](#referring-to-properties) - Property | | | |[e1`[` e2 `]`](#indexing) - Index | | | |[e1`.{ `e2 `}`](#projecting-across-collections) - Projection | | | |[e1`.{?` e2`}`](#selecting-from-collections) - Selection | | | |[e1`.(`e2`)`](#chained-subexpressions) - Subexpression evaluation | | | |[e1`(`e2`)`](#expression-evaluation) - Expression evaluation | | | |[constant](#constants) - Constant |Basic expressions |Only property references (`property`), indexes (`[e]`), and variable references (`#variable`) can be passed as top-level expressions to `setValue`. For indexes, `getValue` is called on `e`, and then the result is used as the property "name" (which might be a `String` or any other kind of object) to set in the current target object. Variable and property references are set more directly.| |[`(` e `)`](#parenthetical-expressions) - Parenthesized expression | | | |[method`(`args`)`](#calling-methods) - Method call | | | |[property](#referring-to-properties) - Property reference | | | |[`[` e `]`](#indexing) - Index reference | | | |[`{`e`,` ... `}`](#collection-construction) - List creation | | | |[`#`variable](#variable-references) - Context variable reference | | | |[`@`class`@`method`(`args`)`](#calling-static-methods) - Static method reference| | | |[`@`class`@`field](#getting-static-fields) - Static field reference | | | |[`new` class`(`args`)`](#calling-constructors) - Constructor call | | | |[`new `array-component-class`[] {` e`,` ... `}`](#native-arrays) - Array creation| | | |[`#{` e1 `:` e2`,` ... `}`](#maps) - Map creation | | | |[`#@`classname`@{`e1 `:` e2`,` ... `}`](#maps) - Map creation with specific subclass| | | |[`:[` e `]`](#pseudo-lambda-expressions) - Lambda expression definition | | | > **Note** These operators are listed in reverse precedence order [^1]: This is only true with the default ClassResolver in place. With a custom class resolver packages can be mapped in such a way that more Java-like references to classes can be made. Refer to the OGNL Developer's Guide for details on using `ClassResolver` class. ================================================ FILE: docs/NullSafeOperator.md ================================================ # Null-Safe Navigation Operator (?.) ## Overview The null-safe navigation operator `?.` provides a safe way to navigate object graphs without throwing exceptions when encountering null intermediate values. This feature is inspired by similar operators in Kotlin (`?.`), C# (`?.`), TypeScript (`?.`), Groovy (`?.`), and PHP 8.0 (`?->`). ## Motivation ### Current Behavior In standard OGNL, navigating through null objects typically throws an exception: ```java // If user.profile is null, this throws an exception String home = (String) Ognl.getValue("user.profile.home", context, root); ``` Users must use workarounds: 1. Ternary expressions: `user.profile != null ? user.profile.home : null` 2. Custom NullHandler implementations 3. Relying on short-circuit behavior (system property dependent) ### Problems with Current Approaches 1. **Verbose**: Ternary expressions become unwieldy for deep navigation 2. **Global**: NullHandler affects all property access for a class 3. **Inconsistent**: Short-circuit behavior varies by configuration 4. **Unclear**: Not explicit in the expression what is null-safe ### Proposed Solution Explicit null-safe operator that works at any level: ```java // Returns null gracefully if any intermediate value is null String home = (String) Ognl.getValue("user?.profile?.home", context, root); ``` ## Syntax ### Operator: `?.` We chose `?.` (question-dot, "safe navigation") for the following reasons: 1. **Language consistency**: Aligns with Kotlin, TypeScript, C#, and Groovy's `?.` operator 2. **Familiar syntax**: Developers coming from other languages will immediately recognize the pattern 3. **No ambiguity**: No conflict with existing `?:` ternary operator 4. **Precedence clarity**: Aligns with existing dot operator parsing ### Grammar The operator is added to the navigation chain production in the JavaCC grammar: ``` navigationChain() := primaryExpression() ( "." | "?." // New: null-safe navigation | "[" "]" | ... )* ``` ## Semantics ### Basic Property Access ```java // If obj is null, returns null instead of throwing exception obj?.property obj?.method() obj?.field ``` ### Chaining Each `?.` operator is independent and short-circuits only that specific access: ```java // Regular chain: exception if any value is null a.b.c.d // Fully null-safe: null if any value is null a?.b?.c?.d // Mixed: exception if 'a' is null, null if 'b' is null, exception if 'c' is null a.b?.c.d ``` ### Method Calls Null-safe operator works with method invocations: ```java user?.getProfile()?.getHome() // If user is null: does not call getProfile(), returns null // If user.getProfile() is null: does not call getHome(), returns null ``` ### Indexed Access **Note**: Direct null-safe indexing (e.g., `array?.[0]`) is not supported in Phase 1 because indexing doesn't use dot notation. Instead, use null-safe navigation before indexing: ```java user?.addresses[0] // Null if user is null; exception if addresses is null but user is not array?.length // Null if array is null (property access works) ``` ### Edge Cases #### 1. Null Root Object ```java null?.property // Returns null ``` #### 2. Multiple Null-Safe Operators ```java a?.b?.c?.d // Each level checked independently ``` #### 3. Combination with Other Operators ```java // Ternary with safe access (OGNL doesn't have ?: Elvis operator) (user?.profile != null ? user?.profile : defaultProfile).home // Conditional with safe access user?.profile?.verified ? "yes" : "no" // Method call results user?.getScores().{? #this > 50} // Projection on potentially null collection ``` #### 4. Assignment (Setter) Semantics **Phase 1 (Current)**: Read-only support ```java user?.profile?.home // Supported: returns null if any part is null ``` **Future Phase**: Setter semantics (to be determined) ```java user?.profile?.home = "new value" // TBD: Should this be a no-op if user or profile is null? ``` Decision deferred to gather user feedback on expected behavior. ## Implementation Design ### 1. Parser Level (ognl.jj) #### Token Definition ```java TOKEN: { < NULL_SAFE_DOT: "?." > } ``` #### Grammar Production Modify `navigationChain()` to recognize `?.`: ```java void navigationChain() : { boolean nullSafe = false; } { primaryExpression() ( ( "." { nullSafe = false; } | "?." { nullSafe = true; } ) #Chain( 2) ( ( LOOKAHEAD(2) methodCall() | propertyName() ) | ( LOOKAHEAD(2) projection() | selection() ) | "(" expression() ")" ) { // Set null-safe flag on the created ASTChain node ((ASTChain)jjtree.peekNode()).setNullSafe(nullSafe); } | // ... existing productions for indexing, etc. )* } ``` ### 2. AST Node Level #### ASTChain Modifications ```java public class ASTChain> extends SimpleNode { private boolean nullSafe = false; // New field public void setNullSafe(boolean nullSafe) { this.nullSafe = nullSafe; } public boolean isNullSafe() { return nullSafe; } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = source; // Null-safe check at chain start if (nullSafe && result == null) { return null; } // Existing short-circuit logic... if (shortCircuit && result == null && !(parent instanceof ASTIn)) { return null; } for (int i = 0, ilast = children.length - 1; i <= ilast; ++i) { // Null-safe check during iteration if (nullSafe && result == null) { return null; } // ... rest of existing logic result = children[i].getValue(context, result); } return result; } } ``` #### ASTProperty Modifications ```java public class ASTProperty> extends SimpleNode { private boolean nullSafe = false; // New field public void setNullSafe(boolean nullSafe) { this.nullSafe = nullSafe; } public boolean isNullSafe() { return nullSafe; } protected Object getValueBody(C context, Object source) throws OgnlException { // Null-safe check if (nullSafe && source == null) { return null; } Object property = getProperty(context, source); Object result = OgnlRuntime.getProperty(context, source, property); if (result == null && !nullSafe) { // Only invoke NullHandler if not using null-safe operator NullHandler nullHandler = OgnlRuntime.getNullHandler( OgnlRuntime.getTargetClass(source)); result = nullHandler.nullPropertyValue(context, source, property); } return result; } } ``` ### 3. Bytecode Compiler Level The `toGetSourceString` and `toSetSourceString` methods need to handle null-safe chains: ```java public String toGetSourceString(C context, Object target) { if (nullSafe && target == null) { return "null"; } if (nullSafe) { // Generate null-check wrapped access // Example: (target != null ? target.property : null) String result = generateNullSafeAccess(context, target); return result; } // ... existing logic for normal access } private String generateNullSafeAccess(C context, Object target) { String targetExpr = "..."; // Get target expression String accessExpr = "..."; // Get access expression return String.format("(%s != null ? %s : null)", targetExpr, accessExpr); } ``` ### 4. AST Node Flag Propagation When parsing `?.property`, the parser needs to: 1. Create an ASTChain node (as usual) 2. Mark it as `nullSafe = true` 3. The ASTChain evaluates normally but checks for null before navigation ## Interaction with Existing Features ### 1. Short-Circuit Behavior The null-safe operator is **explicit** and **independent** from the existing short-circuit behavior: ```java // System property: ognl.chain.short-circuit=true (default) user.profile.home // May return null due to short-circuit // System property: ognl.chain.short-circuit=false user.profile.home // Throws exception if profile is null // Null-safe operator: always returns null regardless of system property user?.profile?.home // Always returns null if any part is null ``` ### 2. NullHandler Null-safe operator **bypasses** NullHandler for that specific access: ```java // Without null-safe: NullHandler invoked if property returns null user.profile // NullHandler.nullPropertyValue() called if profile is null // With null-safe: NullHandler NOT invoked user?.profile // Simply returns null, NullHandler not consulted ``` **Rationale**: The null-safe operator expresses explicit intent to handle null. If users want custom null handling, they should use regular `.` access. ### 3. Projection and Selection Null-safe operator protects the collection access: ```java // Exception if users is null users.{name} // Null if users is null users?.{name} // Null if users is null, otherwise projection proceeds normally users?.{? #this.age > 18} ``` ### 4. Method Invocation Null-safe prevents method calls on null objects: ```java // Exception if user is null user.getProfile().getHome() // Null if user is null (getProfile not called) user?.getProfile()?.getHome() ``` ## Testing Strategy ### Test Categories #### 1. Basic Property Access (10 tests) - Null root object: `null?.property` - Non-null access: `object?.property` - Nested null-safe: `a?.b?.c` - Mixed safe/unsafe: `a.b?.c.d` #### 2. Method Calls (8 tests) - Null object method: `null?.method()` - Method chain: `obj?.method1()?.method2()` - Method with arguments: `obj?.method(arg1, arg2)` - Mixed property and method: `obj?.property?.method()` #### 3. Indexed Access (8 tests) - Note: Direct null-safe indexing not supported in Phase 1 - Null-safe before index: `user?.addresses[0]` - Property access on arrays: `array?.length` - Map value access: `map?.get("key")` #### 4. Collection Operations (8 tests) - Null projection: `null?.{name}` - Null selection: `null?.{? #this > 0}` - Safe projection: `list?.{name}` - Safe selection: `list?.{? #this.verified}` #### 5. Combined Operators (10 tests) - Ternary: `obj?.prop != null ? obj?.prop : 'default'` - Conditional: `obj?.prop ? 'yes' : 'no'` - Assignment context: `#var = obj?.prop` - Arithmetic: `(obj?.value != null ? obj?.value : 0) + 10` #### 6. Edge Cases (12 tests) - Multiple consecutive: `a?.b?.c` - Deep nesting: 10+ levels of null-safe access - Variable references: `#root?.toString()` - Static method results: `@Class@staticMethod()?.property` - Mixed safe/unsafe chains - Null root handling #### 7. Bytecode Compilation (10 tests) - Compiled null-safe access - Performance comparison with regular access - Null-safe in loops - Complex expressions compilation #### 8. Interaction Tests (10 tests) - With NullHandler - With short-circuit enabled/disabled - With MemberAccess restrictions - With TypeConverter #### 9. Error Cases (6 tests) - Parser accepts `?.` syntax - Complex null-safe expressions - Mixed with projections and selections #### 10. Regression Tests (10 tests) - All existing tests must pass - Performance benchmarks - Memory usage patterns **Total: 92 comprehensive tests** ensuring 100% code coverage ### Coverage Requirements 1. **Line Coverage**: 100% of new code 2. **Branch Coverage**: 100% of null-safe conditionals 3. **Path Coverage**: All combinations of null/non-null values 4. **Mutation Testing**: Verify tests catch intentional bugs ### Test Execution ```bash # Run tests with coverage mvn clean test -Pcoverage # Generate coverage report mvn jacoco:report # Verify coverage thresholds # New classes/methods must have 100% coverage ``` ## Performance Considerations ### Expected Overhead The null-safe operator introduces minimal overhead: 1. **Parsing**: One additional token check 2. **AST Construction**: One boolean flag per chain node 3. **Evaluation**: One null check per `?.` operator ### Optimization Strategies 1. **Compiler optimization**: Generate efficient bytecode with single null check 2. **Short-circuit early**: Return immediately on first null encounter 3. **No exception overhead**: Avoid try-catch blocks in null-safe paths ### Benchmarks Benchmark comparison (to be measured): ```java // Baseline: try-catch try { return user.profile.home; } catch (NullPointerException e) { return null; } // Ternary chain (current workaround) user != null ? (user.profile != null ? user.profile.home : null) : null // Null-safe operator (new) user?.profile?.home ``` Expected: Null-safe operator should be **faster** than try-catch and **comparable** to ternary chain. ## Future Enhancements ### Phase 2: Write Support Setter semantics for null-safe chains: ```java // Option 1: Silent no-op (Kotlin style) user?.profile?.home = "new" // Does nothing if user or profile is null // Option 2: Create intermediate objects user?.profile?.home = "new" // Creates profile if null (risky) // Option 3: Error user?.profile?.home = "new" // Compile error or runtime exception ``` **Decision**: Deferred pending community feedback. ### Phase 3: Elvis Operator Note: OGNL currently doesn't support the `?:` Elvis operator. If added in the future: ```java user?.profile?.home ?: "default" // Future: null coalescing user?.profile?.home ?= "default" // Future: assign only if null ``` ### Phase 4: Null-Safe Method Reference ```java users?.{?.getName()} // Null-safe within projection ``` ## Migration Guide ### Existing Code All existing OGNL expressions remain unchanged and fully compatible. ### Adopting Null-Safe Operator **Before:** ```java // Verbose ternary String home = (user != null && user.getProfile() != null) ? user.getProfile().getHome() : null; // Or with try-catch try { home = user.getProfile().getHome(); } catch (NullPointerException e) { home = null; } ``` **After:** ```java // Concise and explicit String home = Ognl.getValue("user?.getProfile()?.getHome()", context, root); ``` ### Interaction with NullHandler If you have custom NullHandler implementations: ```java // Old: NullHandler always invoked on null properties class MyNullHandler implements NullHandler { public Object nullPropertyValue(C context, Object target, Object property) { return "DEFAULT"; // Always called when property is null } } // New behavior: user.profile // Returns "DEFAULT" via NullHandler if profile is null user?.profile // Returns null, NullHandler NOT invoked ``` **Recommendation**: Use `?.` when you want explicit null returns. Use regular `.` when you want NullHandler behavior. ## Examples ### Real-World Use Cases #### 1. User Profile Navigation ```java // Get user's home city, null if any intermediate value is null String city = Ognl.getValue("user?.profile?.address?.city", context, root); ``` #### 2. Optional Configuration ```java // Get optional config value with ternary fallback Integer timeout = Ognl.getValue("config?.database?.timeout != null ? config?.database?.timeout : 3000", context, root); ``` #### 3. Collection Processing ```java // Get names of active users, null if users list is null List names = Ognl.getValue("users?.{? #this.active}?.{name}", context, root); ``` #### 4. API Response Handling ```java // Navigate JSON-like structure safely // Note: Direct indexing with ?. not supported in Phase 1 Object data = Ognl.getValue("response?.body?.data?.items[0]?.id", context, root); ``` #### 5. Conditional UI Rendering ```java // Check if user has admin role, false if user is null boolean isAdmin = Ognl.getValue("user?.roles != null && user?.roles.contains('ADMIN')", context, root); ``` ## References ### Similar Features in Other Languages 1. **Kotlin**: `?.` operator - Docs: https://kotlinlang.org/docs/null-safety.html 2. **C#**: `?.` operator (Null-conditional operator) - Docs: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and- 3. **TypeScript**: `?.` (Optional chaining) - Docs: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining 4. **Groovy**: `?.` (Safe navigation operator) - Docs: https://groovy-lang.org/operators.html#_safe_navigation_operator 5. **PHP 8.0**: `?->` (Nullsafe operator) - Docs: https://php.watch/versions/8.0/null-safe-operator 6. **Swift**: `?` (Optional chaining) - Docs: https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html ## Version History - **v3.6.0** (Proposed): Initial implementation of `?.` null-safe operator - Basic property and method access - Comprehensive test coverage - Documentation and migration guide --- **Document Status**: Draft **Last Updated**: 2025-11-07 **Authors**: OGNL Development Team **Related Issues**: [To be added] ================================================ FILE: docs/VersionNotes.md ================================================ # Version Notes ## Release notes - version 3.4.3, 3.3.5 (2024-04-19) * Fixes potential security vulnerability in accessing public fields via ObjectPropertyAccessor, see [#265](../../../issues/265), [#264](../../../issues/264) - thanks to jefferyxhy ## Release notes - version 3.3.4 (2022-09-05) * Fixes IllegalArgumentException: Passes instance of MemberAccess to OgnlContext, see [#162](../../../issues/162) - thanks to oosato-im ## Release notes - version 3.3.3 (2022-07-10) * Fixes IllegalArgumentException: Can't decide which method to use, see [#159](../../../pull/159) - thanks to harawata ## Release notes - version 3.3.2 (2022-01-09) * Avoid illegal reflective access when possible and use public method, see [#144](../../../pull/144) - thanks to harawata ## Release notes - version 3.3.1 (2021-12-30) * avoid collecting stacktrace when building an OgnlException, see [#138](../../../pull/138) - thanks to davoustp ## Release notes - version 3.3.0 (2021-11-19) * as from this release OGNL requires Java 8 at least * fixes issue with Eclipse JDT by explicitly casting the Enum#compareTo argument, see [#139](../../../pull/139) - thanks to harawata ## Release notes - version 3.2.21 (2021-05-13) * fixes issue with VarArgs, see [#125](../../../issues/125) & [#126](../../../pull/126) - thanks to lukaszlenart ## Release notes - version 3.2.20, 3.1.29 (2021-04-05) * fixes `OgnlOps#equal`, see [#116](../../../pull/116) & [#123](../../../pull/123) - thanks to aleksandr-m ## Release notes - version 3.2.19 (2021-03-18) * uses `MemberAccess` to create a new default context to avoid NPE [#118](../../../pull/118) - thanks to zhuster ## Release notes - version 3.2.18 (2020-12-18) * un-deprecates previously deprecated API by providing missing instance of `MemberAccess` [#114](../../../issues/114) - thanks to lukaszlenart ## Release notes - version 3.2.17 (2020-12-05) * makes AST classes public [#115](../../../pull/115) - thanks to sebthom * un-deprecates previously deprecated API by providing missing instance of `MemberAccess` [#114](../../../issues/114) - thanks to lukaszlenart ## Release notes - version 3.2.16 (2020-11-14) * adds support for null varargs [#113](../../../pull/113) - thanks to lukaszlenart * updates `isMethodCallable()` logic, re-introduce its usage for `getReadMethod()` [#110](../../../pull/110) - thanks to JCgH4164838Gh792C124B5 * introduces `AbstractMemberAccess` to allow create it on-fly [#109](../../../pull/109) - thanks to lukaszlenart * bumps junit from 4.12 to 4.13.1 [#108](../../../pull/108) - thanks to dependabot * minor cleanups related to previous 3.1.x merges [#107](../../../pull/107) - thanks to JCgH4164838Gh792C124B5 * fixes resolve race condition when there are to many threads since [#106](../../../pull/106) - thanks to rolandhe ## Release notes - version 3.2.15 * fixes `OgnlRuntime#getReadMethod()` returns `null` if the method is a bridge method [#104](../../../pull/104) - thanks to harawata ## Release notes - version 3.2.14 * deprecated constructor always throws an exception [#81](../../../issues/81),[#101](../../../pull/101) - thanks to JCgH4164838Gh792C124B5 ## Release notes - version 3.2.13, 3.1.28 * fixes Enum comparison failure [#98](../../../pull/98),[#99](../../../pull/99) - thanks to JCgH4164838Gh792C124B5 ## Release notes - version 3.2.12 * DefaultClassResolver should resolve classes in the default package [#93](../../../pull/93) - thanks to Iwao AVE! * Resolves problem with setting varargs parameter [#92](../../../pull/92) - thanks to Łukasz Lenart * Various minor cleanup changes [#85](../../../pull/85) - thanks to JCgH4164838Gh792C124B5 * add expression max length functionality to improve security [#82](../../../pull/82) - thanks to Yasser Zamani * plus additional enhancements related to max length functionality [#87](../../../pull/87) - thanks to JCgH4164838Gh792C124B5 * improves getter/setter detection algorithm [#75](../../../pull/75) - thanks to JCgH4164838Gh792C124B5 * enhances cache clearing [#77](../../../pull/77) - thanks to JCgH4164838Gh792C124B5 * does not fail on getDeclaredXXX when user has used a SecurityManager [#79](../../../pull/79) - thanks to Yasser Zamani ## Release notes - version 3.1.26 * add expression max length functionality to improve security [#82](../../../pull/82) - thanks to Yasser Zamani * plus additional enhancements related to max length functionality [#87](../../../pull/87) - thanks to JCgH4164838Gh792C124B5 ## Release notes - version 3.1.25 * improves getter/setter detection algorithm [#75](../../../pull/75) - thanks to JCgH4164838Gh792C124B5 * enhances cache clearing [#77](../../../pull/77) - thanks to JCgH4164838Gh792C124B5 * does not fail on getDeclaredXXX when user has used a SecurityManager [#79](../../../pull/79) - thanks to Yasser Zamani ## Release notes - version 3.2.11 * Fixes to compare non-comparable objects by equals only [#78](../../../issues/78) - thanks to peteruhnak ## Release notes - version 3.1.24 * Adds optional Security Manager to allow run expressions in a sandbox [#69](../../../pull/69) - thanks to Yasser Zamani ## Release notes - version 3.1.22 * Restores unrestricted access to public static fields [#67](../../../pull/67) - thanks to JCgH4164838Gh792C124B5 ## Release notes - version 3.2.10 * Upgrades to Javassist 3.24.1 to restore support for Java 7 [#65](../../../issues/65) - thanks to JCgH4164838Gh792C124B5 ## Release notes - version 3.1.21, 3.2.9 * Code clean up [#63](../../../issues/63), [#64](../../../issues/64) - thanks to JCgH4164838Gh792C124B5 ## Release notes - version 3.1.20 * Minor cleanups for `DefaultMemberAccess` `restore` method [#61](../../../pull/61) - thanks to JCgH4164838Gh792C124B5 ## Release notes - version 3.1.19, 3.2.8 * `MemberAccess` does not support private static field. [#59](../../../issues/59) - thanks to hengyunabc ## Release notes - version 3.1.18 * `getDeclaredMethods()` searches super-interfaces recursively. [#55](../../../pull/55) - thanks to Iwao AVE! * allows override a strategy for loading a class on `DefaultClassResolve` [#50](../../../pull/50) - thanks to Iwao AVE! ## Release notes - version 3.1.18, 3.2.7 * `getDeclaredMethods()` searches super-interfaces recursively. [#55](../../../pull/55) - thanks to Iwao AVE! ## Release notes - version 3.2.6 * allows override a strategy for loading a class on `DefaultClassResolve` [#50](../../../pull/50) - thanks to kazuki43zoo ## Release notes - version 3.1.17, 3.2.5 * supports concurrency in `DefaultClassResolver` [#46](../../../pull/46) - thanks to kazuki43zoo ## Release notes - version 3.1.16, 3.2.4 * collects only default methods when scanning interfaces [#40](../../../pull/40) - thanks to Iwao AVE! ## Release notes - version 3.2.3 - WIP (new changes are coming) * makes `OgnlContext` a bit more immutable * `TypeConverter` can be set only when creating a new context, the setter won't work anymore * Implementation of the `MemberAccess` is required when crearting a new context, you must always provide your own * `DefaultMemberAccess` is only available in tests, it won't be used when there was no custom `MemberAccess` provided, an exception will be thrown in such case * sets source and target in `pom.xml` to Java 1.7 * makes better decisions on methods first call [#39](../../../pull/39) - thanks to Yasser Zamani * fixes access to property which reads method is Java 8 default method [#33](../../../pull/33) - thanks to Yanming Zhou ## Release notes - version 3.1.15, 3.0.21 * makes better decisions on methods first call [#36](../../../pull/36), [#38](../../../pull/38) - thanks to Yasser Zamani ## Release notes - version 3.1.14, 3.0.20 * drops access to `#context` and `_classResolver` via a magic keys - thanks to Łukasz Lenart ## Release notes - version 3.1.12 * fixes issue with returning the `hasCode` method when looking for a field `code` [#32](../../../issues/32) - thanks to Łukasz Lenart ## Release notes - version 3.1.11 * fixes issue with returning default methods from interfaces implemented by parent class [#30](../../../issues/30) - thanks to Vlastimil Dolejš ## Release notes - version 3.1.10, 3.0.19 * Does not treat negative numbers as an arithmetic operation [#28](../../../issues/28) - thanks to Łukasz Lenart ## Release notes - version 3.1.9, 3.0.18 * Drops access to `_memeberAccess` field via a magic key - thanks to Łukasz Lenart ## Release notes - version 3.1.8, 3.0.17, 3.0.6.2 * Exposes flags to allow check if an expression is a chain or an arithmetic operation or a simple method - thanks to Łukasz Lenart ## Release notes - version 3.1.6 * fixes automatic type conversion to avoid `double boxing` [#25](../../../issues/25)/[#26](../../../pull/26) - thanks to Christian Niessner from [secadm GmbH](http://www.secadm.de/) ## Release notes - version 3.1.5 * fixes issue with selecting overloaded methods [#23](../../../issues/23)/[#24](../../../pull/24) - thanks to Christian Niessner from [secadm GmbH](http://www.secadm.de/) ## Release notes - version 3.1.4 * fixes issue with executing expressions on Java 8 plus adds an `java like` method matching [#19](../../../pull/19) - thanks to marvkis ## Release notes - version 3.1.3, 3.0.14, 3.0.6.1 * Exposes flag to allow check if an expression is a sequence of simple expressions - thanks to Łukasz Lenart ## Release notes - version 3.1.2 * Fixes accessing statics within Enums [OGNL-158](https://issues.apache.org/jira/browse/OGNL-158) - thanks to Aleksandr Mashchenko ## Release notes - version 3.1.1, 3.0.13 * OgnlRuntime.invokeMethod can throw IllegalAccessException because of hash collisions was fixed [OGNL-252](https://issues.apache.org/jira/browse/OGNL-252) - thanks to Carlos Saona ## Release notes - version 3.1 * support for boolean expression in Strings was added, this can break backward compatibility [#8](../../../issues/8) - thanks to Daniel Fernández ## Release notes - version 3.0.12 * lots of optimizations which should improve overall performance [#9](../../../pull/9), [#10](../../../pull/10), [#11](../../../pull/11), [#12](../../../pull/12) - thanks to Daniel Fernández * OGNL supports default methods in interfaces (Java 8) [OGNL-249](https://issues.apache.org/jira/browse/OGNL-249) ## Release notes - version 3.0.11 * fixes problem with cacheKey too expensive to create [WW-4485 ](https://issues.apache.org/jira/browse/WW-4485 ) - thanks to Jasper Rosenberg ## Release notes - version 3.0.10 * regression bug in ognl for "is..." property getters [WW-4462](https://issues.apache.org/jira/browse/WW-4462) - if expression doesn't end with `()` is considered as a name of property a not the method itself thanks to Jasper Rosenberg ## Release notes - version 3.0.9 * replaced IntHashMap with ConcurrentMap to avoid deadlocks [WW-4451](https://issues.apache.org/jira/browse/WW-4451) - thanks to Jasper Rosenberg ## Release notes - version 3.0.8 * added better capitalization logic for methods [WW-3909](https://issues.apache.org/jira/browse/WW-3909) - thanks to Iwen.ma ## Release notes - version 3.0.7 * uses better method to calculate method's cache key [WW-4113](https://issues.apache.org/jira/browse/WW-4113) - thanks to Kevin Su ## Release notes - version 3.0.6 * important performance improvement [OGNL-224](https://issues.apache.org/jira/browse/OGNL-224) - thanks to Pelladi Gabor * race condition fix [OGNL-226](https://issues.apache.org/jira/browse/OGNL-226) - thanks to Johno Crawford ## Release notes - version 3.0.5 * partially reverts previous changes to allow OGNL to work in environment with Security Manager enabled [WW-3746](https://issues.apache.org/jira/browse/WW-3746) ## Release notes - version 3.0.4 * Adds possibility to discover eval chain ## Release notes - version 3.0.3 * small fix to improve performance [WW-3580](https://issues.apache.org/jira/browse/WW-3580) ## Release notes - version 3.0.2 * small fix to solve a problem with compiling under JDK5 ## Release notes - version 3.0.1 * Javassist added back as a dependency [WW-3544](https://issues.apache.org/jira/browse/WW-3544) ================================================ FILE: docs/plans/ISSUE_18_COMPILED_MODE_PARITY.md ================================================ # Issue #18 — Compiled vs Interpreted Mode Parity ## Context Issue [#18](https://github.com/orphan-oss/ognl/issues/18) reports 85 test failures + 3 errors when running OGNL expressions in compiled mode vs interpreted mode. [PR #555](https://github.com/orphan-oss/ognl/pull/555) fixed 3 compiler bugs (ASTConst Long/Float suffixes, ASTOr boolean boxing). [PR #556](https://github.com/orphan-oss/ognl/pull/556) added 82 dual-mode tests confirming categories 1-10 largely work. ## Current State (2026-04-01) ### Dual-Mode Test Coverage: 225 tests (216 active, 9 disabled) Comprehensive dual-mode testing across all categories reveals that the compiled mode works correctly for the vast majority of expressions. The original estimate of ~80 remaining failures was significantly higher than reality. ### Confirmed Working (all pass in both modes) | Category | Tests | Description | |----------|-------|-------------| | Constants | 11 | int, long, double, float, true/false, null, string, char, hex, octal | | Integer arithmetic | 10 | negation, add, subtract, multiply, divide, modulus, precedence | | Double arithmetic | 6 | negation, add, multiply, divide | | Bitwise operations | 6 | not, shifts, xor, or | | Logical expressions | 12 | not, comparisons, equality, ternary, short-circuit | | Property access | 21 | simple, nested, array, map (dot/bracket/concat), boolean, ternary, string concat | | Setter paths | 8 | map set, list index, property, string, special index ($) | | Setter with conversion | 9 | int↔double, int↔string, string↔float | | Index access | 16 | variable, object, method, generic, self, boolean array, tab search, 2D | | Dynamic subscripts | 4 | ^, $, \| on maps and arrays | | Complex expressions | 10 | #this, #root, sub-expressions, nested ternaries, list literals | | Array elements | 7 | char array, list literals, set with int/string, root array access | | Method calls (Root) | 10 | no-arg, string-arg, static, format with conversion, nested | | Method calls (Simple) | 23 | hashCode, boolean ternary, varargs, enum, messages.format variants, testMethods | | Interface inheritance | 19 | myMap access, BeanProvider, custom list, null keys, bracket access | | Primitive null handling | 7 | set null on int/float/boolean, verify defaults | | Indexed properties | 10 | getValues, indexed access, ^/\|/$, getTitle, source.total | | Generics | 2 | service.getFullMessageFor, ids set/get | | IndexedSetObject | 1 | thing["x"].val set/get | ### Known Compiler Limitations (13 `@Disabled` tests) | # | Category | Tests | Root Cause | Fixable? | |---|----------|-------|------------|----------| | 1 | BigDecimal arithmetic | 8 | Java operators can't apply to BigDecimal in generated source | Hard (redesign) | | 2 | BigInteger arithmetic | — | (covered by BigDecimal disabled class) | Hard (redesign) | | 3 | ~~instanceof expressions~~ | ~~2~~ | ~~Fixed: set `_noRoot` flag to prevent root expression prefix on self-contained instanceof source~~ | **Fixed** | | 4 | ~~Float subtraction~~ | ~~1~~ | ~~Fixed: `NumericExpression.coerceToNumeric()` now appends numeric literal suffix for ASTConst~~ | **Fixed** | | 5 | ~~String escaping in concat~~ | ~~1~~ | ~~Fixed: replaced `"` → `'` substitution with proper `\"` escaping in ASTAdd~~ | **Fixed** | | 6 | Side-effect methods | 1 | Compiler evaluates expression during type inference, double-calling side-effect methods like EvenOdd.getNext() | Hard (architectural) | ### What Happened to the ~80 Failures? The original issue #18 reported 85 failures. Investigation shows: 1. **PR #555 fixed** 3 bugs (ASTConst Long/Float, ASTOr boxing) — these unlocked many expressions 2. **Most categories work correctly** — comprehensive testing of 212 expressions confirms parity 3. **The remaining real failures** are limited to the 9 disabled tests above (string escaping, instanceof, and float subtraction fixed) 4. **The original count** likely included cascading failures where one bug caused multiple test failures ## Remaining Work ### ~~PR: String Escaping Fix (category 5)~~ — DONE **Fixed in PR #TBD.** Root cause: `ASTAdd.toGetSourceString()` lines 208-214 replaced `"` with `'` in string constants during compiled concatenation. Fix: use proper Java string escaping (`\"`) instead of single-quote substitution, and preserve `"` as literal text. ### ~~PR: instanceof Support (category 3)~~ — DONE **Fixed in PR #559.** Root cause: `ASTInstanceof.toGetSourceString()` didn't set `_noRoot` flag, causing `ExpressionCompiler.generateGetter()` to prepend root expression (`$2.`) to the generated source (`true`), producing invalid Java source `$2.true`. ### Deferred: BigDecimal/BigInteger (categories 1-2 in old plan) **Difficulty:** Hard (significant redesign) - Requires compiler to generate `OgnlOps` helper calls instead of Java operators - ~8 `@Disabled` tests covering this limitation - Should be a separate initiative ### Deferred: Side-effect methods during compilation (category 6) **Difficulty:** Hard (architectural) - The compiler evaluates expressions during `toGetSourceString()` for type inference - Methods with side effects (like `EvenOdd.getNext()`) are called during compilation AND at runtime - Would require separating type inference from expression evaluation ## Key Files Reference ### Compiler infrastructure - `ognl/src/main/java/ognl/enhance/ExpressionCompiler.java` — core compiler - `ognl/src/main/java/ognl/OgnlRuntime.java` — `getChildSource()`, `isBoolean()` ### AST nodes (source generation) - `ognl/src/main/java/ognl/ASTProperty.java` — property get/set - `ognl/src/main/java/ognl/ASTMethod.java` — method calls - `ognl/src/main/java/ognl/ASTChain.java` — expression chains - `ognl/src/main/java/ognl/ASTConst.java` — constants (string escaping bug here) - `ognl/src/main/java/ognl/ASTAdd.java` — string concatenation - `ognl/src/main/java/ognl/ASTInstanceof.java` — instanceof (broken) ### Test infrastructure - `ognl/src/test/java/ognl/test/DualModeEvaluationTest.java` — 225 dual-mode tests ## Verification ```bash ./mvnw test -pl ognl # Full suite — 951 tests, must stay green ./mvnw test -pl ognl -Dtest=DualModeEvaluationTest # Dual-mode tests — 225 tests (212 active + 13 disabled) ``` ================================================ FILE: docs/plans/JDK25_FORWARD_COMPATIBILITY.md ================================================ # OGNL JDK 25 Forward-Compatibility Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Make OGNL forward-compatible with JDK 25 while keeping Java 17 as the minimum compilation and runtime target. **Architecture:** Phased approach — first add JDK 25 to CI for visibility, then collapse the obsolete pre-JDK9/JDK9+ handler abstraction (Java 17 is now the minimum), deprecate SecurityManager-dependent class, verify Javassist compatibility, and document `--add-opens` requirements. **Tech Stack:** Java 17+ (source/target), Maven, Javassist, JUnit Jupiter 6.x --- ## Context JDK 25 (GA September 2025) introduces breaking changes affecting reflection-heavy libraries like OGNL: - **JEP 486 (JDK 24):** SecurityManager permanently disabled — `BasicPermission` deprecated for removal - **JEP 498 (JDK 23+):** `sun.misc.Unsafe` memory-access methods emit warnings, eventual removal - **Stronger module encapsulation:** `setAccessible(true)` throws `InaccessibleObjectException` on strongly encapsulated members without `--add-opens` - **Class file version 69:** Javassist must support JDK 25 bytecode format OGNL is already partially prepared (module-aware `isLikelyAccessible()`, `canAccess()` usage, `--add-opens` in surefire). However, the `AccessibleObjectHandler` / `AccessibleObjectHandlerJDK9Plus` abstraction was designed for pre-JDK9 vs JDK9+ differentiation — **since OGNL now requires Java 17, this entire abstraction layer is obsolete**. It also contains dead `sun.misc.Unsafe` code that references APIs being removed. --- ### Task 1: Add JDK 25 to CI Matrix **Files:** - Modify: `.github/workflows/maven.yml:35` - [ ] **Step 1: Update CI matrix** ```yaml # Change line 35 from: java: [ '17', '21' ] # To: java: [ '17', '21', '25' ] ``` - [ ] **Step 2: Run local build on JDK 25 to establish baseline** Run: `./mvnw -B verify` with JDK 25 Expected: Capture any failures — this establishes the baseline for remaining tasks. - [ ] **Step 3: Commit** ```bash git add .github/workflows/maven.yml git commit -m "ci: add JDK 25 to CI test matrix" ``` --- ### Task 2: Collapse AccessibleObjectHandler Abstraction **Files:** - Modify: `ognl/src/main/java/ognl/AccessibleObjectHandler.java` - Delete: `ognl/src/main/java/ognl/AccessibleObjectHandlerJDK9Plus.java` - Modify: `ognl/src/main/java/ognl/OgnlRuntime.java:168-171, 654, 662` - Modify: `ognl/src/test/java/ognl/DefaultMemberAccess.java:35-42` - Test: `./mvnw test -pl ognl` Since Java 17 is the minimum, the pre-JDK9 vs JDK9+ distinction is meaningless. `AccessibleObjectHandlerJDK9Plus` contains dead `sun.misc.Unsafe` code (the `instantiateClazzUnsafe()` method returns `null` at line 76, making the entire Unsafe path dead). After removing the Unsafe code, the handler just calls `accessibleObject.setAccessible(flag)` — a one-liner that doesn't warrant a separate class. #### Approach 1. Give `AccessibleObjectHandler` interface a `default` method implementation (backward-compatible for external implementors) 2. Delete `AccessibleObjectHandlerJDK9Plus` entirely 3. Move the `unsafeOrDescendant()` security check to `OgnlRuntime` (it's used there for stricter invocation mode) 4. Simplify all initialization code - [ ] **Step 1: Write tests verifying current accessible handler behavior** Add to existing test class or new `AccessibleObjectHandlerTest.java`: ```java @Test void testAccessibleObjectHandlerDefaultSetAccessible() throws Exception { AccessibleObjectHandler handler = new AccessibleObjectHandler() {}; // Uses default method Method method = String.class.getMethod("length"); handler.setAccessible(method, true); assertTrue(method.canAccess("test")); } ``` - [ ] **Step 2: Run test to verify it fails (default method doesn't exist yet)** Run: `./mvnw test -pl ognl -Dtest=AccessibleObjectHandlerTest -Dsurefire.failIfNoSpecifiedTests=false` Expected: Compile error — `default` method not yet added - [ ] **Step 3: Add default method to AccessibleObjectHandler interface** Replace `ognl/src/main/java/ognl/AccessibleObjectHandler.java`: ```java package ognl; import java.lang.reflect.AccessibleObject; /** * Provides a mechanism for changing the accessibility of AccessibleObject instances. * * @since 3.1.24 */ public interface AccessibleObjectHandler { /** * Changes the accessibility of the given AccessibleObject. * * @param accessibleObject the AccessibleObject upon which to apply the flag. * @param flag the new accessible flag value. */ default void setAccessible(AccessibleObject accessibleObject, boolean flag) { accessibleObject.setAccessible(flag); } } ``` - [ ] **Step 4: Run test to verify it passes** Run: `./mvnw test -pl ognl -Dtest=AccessibleObjectHandlerTest` Expected: PASS - [ ] **Step 5: Move unsafeOrDescendant() to OgnlRuntime** In `ognl/src/main/java/ognl/OgnlRuntime.java`, add a private static method (near the other security checks around line 160): ```java /** * Check if a class is sun.misc.Unsafe or jdk.internal.misc.Unsafe. * Used by stricter invocation mode to block OGNL expressions from invoking Unsafe methods. */ private static boolean isUnsafeClass(final Class clazz) { String className = clazz.getName(); return "sun.misc.Unsafe".equals(className) || "jdk.internal.misc.Unsafe".equals(className); } ``` Update line 662 from: ```java AccessibleObjectHandlerJDK9Plus.unsafeOrDescendant(methodDeclaringClass)) { ``` to: ```java isUnsafeClass(methodDeclaringClass)) { ``` - [ ] **Step 6: Simplify OgnlRuntime handler initialization** In `OgnlRuntime.java`, change lines 168-171 from: ```java private static final AccessibleObjectHandler _accessibleObjectHandler; static { _accessibleObjectHandler = AccessibleObjectHandlerJDK9Plus.createHandler(); } ``` to: ```java private static final AccessibleObjectHandler _accessibleObjectHandler = new AccessibleObjectHandler() {}; ``` - [ ] **Step 7: Simplify DefaultMemberAccess (test class)** In `ognl/src/test/java/ognl/DefaultMemberAccess.java`, change lines 34-42 from: ```java /* * Assign an accessibility modification mechanism, based on Major Java Version. * Note: Can be overridden using a Java option flag {@link OgnlRuntime#USE_PREJDK9_ACESS_HANDLER}. */ private static final AccessibleObjectHandler _accessibleObjectHandler; static { _accessibleObjectHandler = AccessibleObjectHandlerJDK9Plus.createHandler(); } ``` to: ```java private static final AccessibleObjectHandler _accessibleObjectHandler = new AccessibleObjectHandler() {}; ``` - [ ] **Step 8: Delete AccessibleObjectHandlerJDK9Plus.java** ```bash git rm ognl/src/main/java/ognl/AccessibleObjectHandlerJDK9Plus.java ``` - [ ] **Step 9: Run full test suite** Run: `./mvnw test -pl ognl` Expected: All tests PASS - [ ] **Step 10: Commit** ```bash git add -A git commit -m "refactor: collapse AccessibleObjectHandler abstraction, remove dead Unsafe code Java 17 is the minimum - the pre-JDK9 vs JDK9+ distinction is obsolete. AccessibleObjectHandlerJDK9Plus contained dead sun.misc.Unsafe code (instantiateClazzUnsafe() returned null, making entire Unsafe path dead). The handler now uses a default interface method that delegates to AccessibleObject.setAccessible() directly." ``` --- ### Task 3: Deprecate OgnlInvokePermission **Files:** - Modify: `ognl/src/main/java/ognl/OgnlInvokePermission.java` `OgnlInvokePermission` extends `java.security.BasicPermission`, which is deprecated for removal in JDK 24+ (JEP 486). This class is NOT used anywhere in the OGNL codebase — it exists only for external consumers using SecurityManager policies. - [ ] **Step 1: Add deprecation annotation, suppress removal warning, update Javadoc** ```java /** * BasicPermission subclass that defines a permission token for invoking * methods within OGNL. This does not override any methods (except * constructors) and does not implement actions. It is similar in spirit * to the {@link java.lang.reflect.ReflectPermission} class in that it * guards access to methods. * * @deprecated SecurityManager has been permanently disabled in JDK 24 * (JEP 486) and * {@link java.security.BasicPermission} is deprecated for removal. * This class will be removed in a future OGNL release. */ @SuppressWarnings("removal") @Deprecated(since = "3.5.0", forRemoval = true) public class OgnlInvokePermission extends BasicPermission { ``` - [ ] **Step 2: Compile** Run: `./mvnw compile -pl ognl` Expected: Compiles cleanly (the `@SuppressWarnings("removal")` suppresses `BasicPermission` deprecation warnings on JDK 24+) - [ ] **Step 3: Commit** ```bash git add ognl/src/main/java/ognl/OgnlInvokePermission.java git commit -m "deprecate: mark OgnlInvokePermission for removal (JEP 486)" ``` --- ### Task 4: Fix detectMajorJavaVersion Fallback **Files:** - Modify: `ognl/src/main/java/ognl/OgnlRuntime.java:2702, 2713` - Test: existing tests or new test The `detectMajorJavaVersion()` Javadoc says fallback is 5 and the code returns 5 (line 2713), but the companion `parseMajorJavaVersion()` Javadoc says 17. Since Java 17 is the minimum, the fallback should be 17. - [ ] **Step 1: Update fallback value and Javadoc** Change line 2702: ```java * @return Detected Major Java Version, or 17 (minimum supported version for OGNL) if unable to detect. ``` Change line 2713: ```java majorVersion = 17; // Return minimum supported Java version for OGNL ``` - [ ] **Step 2: Run tests** Run: `./mvnw test -pl ognl` Expected: All PASS - [ ] **Step 3: Commit** ```bash git add ognl/src/main/java/ognl/OgnlRuntime.java git commit -m "fix: update detectMajorJavaVersion fallback from 5 to 17" ``` --- ### Task 5: Verify and Update Javassist Dependency **Files:** - Modify (if needed): `pom.xml:20` (`javassist.version` property, currently `3.30.2-GA`) Javassist 3.30.2-GA supports up to JDK 22 class file format. JDK 25 uses class file version 69. - [ ] **Step 1: Check latest Javassist version** Check Maven Central for latest `org.javassist:javassist` version. - [ ] **Step 2: Run test suite on JDK 25** Run (with JDK 25): `./mvnw test -pl ognl` Look for: `javassist.CannotCompileException`, `UnsupportedClassVersionError`, or class format errors. - [ ] **Step 3: Update Javassist if needed** ```xml 3.31.0-GA ``` - [ ] **Step 4: Verify on both JDK 17 and 25** Run: `./mvnw test -pl ognl` on both JDK 17 and JDK 25 Expected: All PASS on both - [ ] **Step 5: Commit if changed** ```bash git add pom.xml git commit -m "build: update Javassist to support JDK 25 class file format" ``` --- ### Task 6: Audit --add-opens and Update Documentation **Files:** - Modify (if needed): `ognl/pom.xml:52-55` (surefire argLine) - Modify: `CLAUDE.md` - [ ] **Step 1: Run full test suite on JDK 25 and check for access errors** Run (with JDK 25): `./mvnw test -pl ognl 2>&1 | grep -i "InaccessibleObjectException\|IllegalAccessError\|add-opens"` Current `--add-opens` in surefire: - `java.base/java.lang=ALL-UNNAMED` - `java.base/java.util=ALL-UNNAMED` Add any additional opens only if needed based on actual test failures. - [ ] **Step 2: Update CLAUDE.md** Change the language line: ``` - **Language**: Java 17 (CI also tests against Java 21 and 25) ``` - [ ] **Step 3: Run full test suite on JDK 17, 21, and 25** Expected: All PASS on all three - [ ] **Step 4: Commit** ```bash git add ognl/pom.xml CLAUDE.md git commit -m "build: audit --add-opens for JDK 25, update docs" ``` --- ## Verification After all tasks complete: 1. **JDK 17:** `./mvnw clean verify` — must pass (backward compat) 2. **JDK 21:** `./mvnw clean verify` — must pass (current CI) 3. **JDK 25:** `./mvnw clean verify` — must pass (forward compat) 4. **CI green** on all matrix entries 5. **No SonarCloud regressions** on the PR 6. **Compiled expressions work on JDK 25** — tests exercising `ExpressionCompiler` pass ## Out of Scope (Track Separately) - **`module-info.java`**: Adding a proper module descriptor is a larger effort. `Automatic-Module-Name: ognl` is sufficient for now. - **Removing `OgnlInvokePermission`**: Deprecated now, remove in OGNL 4.0. - **`AccessibleObject.setAccessible(AccessibleObject[], boolean)` static method**: Deprecated since JDK 9 but the reflective lookup at `OgnlRuntime.java:203` already handles `NoSuchMethodException` gracefully — no change needed. ================================================ FILE: mvnw ================================================ #!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Maven2 Start Up Batch script # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ] ; then if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ] ; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false; darwin=false; mingw=false case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then export JAVA_HOME="`/usr/libexec/java_home`" else export JAVA_HOME="/Library/Java/Home" fi fi ;; esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then JAVA_HOME=`java-config --jre-home` fi fi if [ -z "$M2_HOME" ] ; then ## resolve links - $0 may be a link to maven's home PRG="$0" # need this for relative symlinks while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG="`dirname "$PRG"`/$link" fi done saveddir=`pwd` M2_HOME=`dirname "$PRG"`/.. # make it fully qualified M2_HOME=`cd "$M2_HOME" && pwd` cd "$saveddir" # echo Using m2 at $M2_HOME fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then javaExecutable="`which javac`" if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=`which readlink` if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then if $darwin ; then javaHome="`dirname \"$javaExecutable\"`" javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" else javaExecutable="`readlink -f \"$javaExecutable\"`" fi javaHome="`dirname \"$javaExecutable\"`" javaHome=`expr "$javaHome" : '\(.*\)/bin'` JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ] ; then if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else JAVACMD="`which java`" fi fi if [ ! -x "$JAVACMD" ] ; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" return 1 fi basedir="$1" wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then wdir=`cd "$wdir/.."; pwd` fi # end of workaround done echo "${basedir}" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then echo "$(tr -s '\n' ' ' < "$1")" fi } BASE_DIR=`find_maven_basedir "$(pwd)"` if [ -z "$BASE_DIR" ]; then exit 1; fi ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found .mvn/wrapper/maven-wrapper.jar" fi else if [ "$MVNW_VERBOSE" = true ]; then echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar" while IFS="=" read key value; do case "$key" in (wrapperUrl) jarUrl="$value"; break ;; esac done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" if [ "$MVNW_VERBOSE" = true ]; then echo "Downloading from: $jarUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" if command -v wget > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" fi wget "$jarUrl" -O "$wrapperJarPath" elif command -v curl > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" fi curl -o "$wrapperJarPath" "$jarUrl" else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" if [ -e "$javaClass" ]; then if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo " - Compiling MavenWrapperDownloader.java ..." fi # Compiling the Java class ("$JAVA_HOME/bin/javac" "$javaClass") fi if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then # Running the downloader if [ "$MVNW_VERBOSE" = true ]; then echo " - Running MavenWrapperDownloader.java ..." fi ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") fi fi fi fi ########################################################################################## # End of extension ########################################################################################## export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} if [ "$MVNW_VERBOSE" = true ]; then echo $MAVEN_PROJECTBASEDIR fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"` [ -n "$MAVEN_PROJECTBASEDIR" ] && MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" ================================================ FILE: mvnw.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Maven2 Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM set title of command window title %0 @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar" FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @REM This allows using the maven wrapper in projects that prohibit checking in binary data. if exist %WRAPPER_JAR% ( echo Found %WRAPPER_JAR% ) else ( echo Couldn't find %WRAPPER_JAR%, downloading it ... echo Downloading from: %DOWNLOAD_URL% powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" echo Finished downloading %WRAPPER_JAR% ) @REM End of extension %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%" == "on" pause if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% exit /B %ERROR_CODE% ================================================ FILE: ognl/pom.xml ================================================ 4.0.0 ognl ognl-parent 3.5.0-SNAPSHOT ognl jar OGNL Core OGNL - Core module of Object Graph Navigation Library org.javassist javassist org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test org.apache.maven.plugins maven-compiler-plugin compile-tests process-test-sources testCompile org.apache.maven.plugins maven-surefire-plugin --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED **/*Test.java org.apache.maven.plugins maven-jar-plugin true true ognl org.apache.maven.plugins maven-source-plugin true true true attach-sources jar-no-fork org.apache.maven.plugins maven-javadoc-plugin none true true 17 https://docs.oracle.com/en/java/javase/17/docs/api/ none true UTF-8 attach-source jar org.codehaus.mojo javacc-maven-plugin ognl-jjtree generate-sources ${project.build.directory}/generated-sources/java 1 false true true javacc install coverage org.jacoco jacoco-maven-plugin prepare-agent prepare-agent argLine report prepare-package report XML org.apache.maven.plugins maven-surefire-plugin ${argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED ================================================ FILE: ognl/src/main/java/ognl/ASTAdd.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import java.io.Serial; import java.math.BigDecimal; import java.math.BigInteger; public class ASTAdd> extends NumericExpression { @Serial private static final long serialVersionUID = 6299295217841613060L; public ASTAdd(int id) { super(id); } public ASTAdd(OgnlParser p, int id) { super(p, id); } public void jjtClose() { flattenTree(); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = children[0].getValue(context, source); for (int i = 1; i < children.length; ++i) result = OgnlOps.add(result, children[i].getValue(context, source)); return result; } public String getExpressionOperator(int index) { return "+"; } boolean isWider(NodeType type, NodeType lastType) { if (lastType == null) return true; //System.out.println("checking isWider(" + type.getGetterClass() + " , " + lastType.getGetterClass() + ")"); if (String.class.isAssignableFrom(lastType.getGetterClass())) return false; if (String.class.isAssignableFrom(type.getGetterClass())) return true; if (parent != null && String.class.isAssignableFrom(type.getGetterClass())) return true; if (String.class.isAssignableFrom(lastType.getGetterClass()) && Object.class == type.getGetterClass()) return false; if (parent != null && String.class.isAssignableFrom(lastType.getGetterClass())) return false; else if (parent == null && String.class.isAssignableFrom(lastType.getGetterClass())) return true; else if (parent == null && String.class.isAssignableFrom(type.getGetterClass())) return false; if (BigDecimal.class.isAssignableFrom(type.getGetterClass()) || BigInteger.class.isAssignableFrom(type.getGetterClass())) return true; if (BigDecimal.class.isAssignableFrom(lastType.getGetterClass()) || BigInteger.class.isAssignableFrom(lastType.getGetterClass())) return false; if (Double.class.isAssignableFrom(type.getGetterClass())) return true; if (Integer.class.isAssignableFrom(type.getGetterClass()) && Double.class.isAssignableFrom(lastType.getGetterClass())) return false; if (Float.class.isAssignableFrom(type.getGetterClass()) && Integer.class.isAssignableFrom(lastType.getGetterClass())) return true; return true; } public String toGetSourceString(C context, Object target) { try { StringBuilder result = new StringBuilder(); NodeType lastType = null; // go through once to determine the ultimate type if ((children != null) && (children.length > 0)) { Class currType = context.getCurrentType(); Class currAccessor = context.getCurrentAccessor(); Object cast = context.get(ExpressionCompiler.PRE_CAST); for (Node child : children) { child.toGetSourceString(context, target); if (child instanceof NodeType && ((NodeType) child).getGetterClass() != null && isWider((NodeType) child, lastType)) { lastType = (NodeType) child; } } context.put(ExpressionCompiler.PRE_CAST, cast); context.setCurrentType(currType); context.setCurrentAccessor(currAccessor); } // reset context since previous children loop would have changed it context.setCurrentObject(target); if ((children != null) && (children.length > 0)) { for (int i = 0; i < children.length; ++i) { if (i > 0) result.append(" ").append(getExpressionOperator(i)).append(" "); String expr = children[i].toGetSourceString(context, target); if (("null".equals(expr)) || (!(children[i] instanceof ASTConst) && (expr == null || expr.trim().length() <= 0))) { expr = "null"; } //System.out.println("astadd child class: " + _children[i].getClass().getName() + " and return expr: " + expr); if (children[i] instanceof ASTProperty) { expr = ExpressionCompiler.getRootExpression(children[i], context.getRoot(), context) + expr; context.setCurrentAccessor(context.getRoot().getClass()); } else if (children[i] instanceof ASTMethod) { String chain = (String) context.get("_currentChain"); String rootExpr = ExpressionCompiler.getRootExpression(children[i], context.getRoot(), context); //System.out.println("astadd chains is >>" + chain + "<< and rootExpr is >>" + rootExpr + "<<"); // dirty fix for overly aggressive casting dot operations if (rootExpr.endsWith(".") && chain != null && chain.startsWith(").")) { chain = chain.substring(1); } expr = rootExpr + (chain != null ? chain + "." : "") + expr; context.setCurrentAccessor(context.getRoot().getClass()); } else if (children[i] instanceof ExpressionNode) { expr = "(" + expr + ")"; } else if ((!(parent instanceof ASTChain)) && children[i] instanceof ASTChain) { String rootExpr = ExpressionCompiler.getRootExpression(children[i], context.getRoot(), context); if (!(children[i].jjtGetChild(0) instanceof ASTProperty) && rootExpr.endsWith(")") && expr.startsWith(")")) expr = expr.substring(1); expr = rootExpr + expr; context.setCurrentAccessor(context.getRoot().getClass()); String cast = (String) context.remove(ExpressionCompiler.PRE_CAST); if (cast == null) cast = ""; expr = cast + expr; } // turn quoted characters into quoted strings if (context.getCurrentType() != null && context.getCurrentType() == Character.class && children[i] instanceof ASTConst) { if (expr.indexOf('\'') >= 0) expr = expr.replaceAll("'", "\""); context.setCurrentType(String.class); } else { if (!ASTVarRef.class.isAssignableFrom(children[i].getClass()) && !(children[i] instanceof ASTProperty) && !(children[i] instanceof ASTMethod) && !(children[i] instanceof ASTSequence) && !(children[i] instanceof ASTChain) && !NumericExpression.class.isAssignableFrom(children[i].getClass()) && !(children[i] instanceof ASTStaticField) && !(children[i] instanceof ASTStaticMethod) && !(children[i] instanceof ASTTest)) { if (lastType != null && String.class.isAssignableFrom(lastType.getGetterClass())) { if (expr.indexOf('"') >= 0) expr = expr.replaceAll("\"", "\\\\\""); expr = "\"" + expr + "\""; } } } result.append(expr); // hanlde addition for numeric types when applicable or just string concatenation if ((lastType == null || !String.class.isAssignableFrom(lastType.getGetterClass())) && !ASTConst.class.isAssignableFrom(children[i].getClass()) && !NumericExpression.class.isAssignableFrom(children[i].getClass())) { if (context.getCurrentType() != null && Number.class.isAssignableFrom(context.getCurrentType()) && !(children[i] instanceof ASTMethod)) { if (children[i] instanceof ASTVarRef || children[i] instanceof ASTProperty || children[i] instanceof ASTChain) result.append("."); result.append(OgnlRuntime.getNumericValueGetter(context.getCurrentType())); context.setCurrentType(OgnlRuntime.getPrimitiveWrapperClass(context.getCurrentType())); } } if (lastType != null) context.setCurrentAccessor(lastType.getGetterClass()); } } if (parent == null || ASTSequence.class.isAssignableFrom(parent.getClass())) { if (getterClass != null && String.class.isAssignableFrom(getterClass)) getterClass = Object.class; } else { context.setCurrentType(getterClass); } try { Object contextObj = getValueBody(context, target); context.setCurrentObject(contextObj); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return result.toString(); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } } ================================================ FILE: ognl/src/main/java/ognl/ASTAnd.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTAnd> extends BooleanExpression { @Serial private static final long serialVersionUID = 2405276998785752132L; public ASTAnd(int id) { super(id); } public ASTAnd(OgnlParser p, int id) { super(p, id); } public void jjtClose() { flattenTree(); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = null; int last = children.length - 1; for (int i = 0; i <= last; ++i) { result = children[i].getValue(context, source); if (i != last && !OgnlOps.booleanValue(result)) break; } return result; } protected void setValueBody(C context, Object target, Object value) throws OgnlException { int last = children.length - 1; for (int i = 0; i < last; ++i) { Object v = children[i].getValue(context, target); if (!OgnlOps.booleanValue(v)) return; } children[last].setValue(context, target, value); } public String getExpressionOperator(int index) { return "&&"; } public Class getGetterClass() { return null; } public String toGetSourceString(C context, Object target) { if (children.length != 2) throw new UnsupportedCompilationException("Can only compile boolean expressions with two children."); String result = ""; try { String first = OgnlRuntime.getChildSource(context, target, children[0]); if (!OgnlOps.booleanValue(context.getCurrentObject())) { throw new UnsupportedCompilationException("And expression can't be compiled until all conditions are true."); } if (!OgnlRuntime.isBoolean(first) && !context.getCurrentType().isPrimitive()) first = OgnlRuntime.getCompiler().createLocalReference(context, first, context.getCurrentType()); String second = OgnlRuntime.getChildSource(context, target, children[1]); if (!OgnlRuntime.isBoolean(second) && !context.getCurrentType().isPrimitive()) second = OgnlRuntime.getCompiler().createLocalReference(context, second, context.getCurrentType()); result += "(ognl.OgnlOps.booleanValue(" + first + ")"; result += " ? "; result += " ($w) (" + second + ")"; result += " : "; result += " ($w) (" + first + ")"; result += ")"; context.setCurrentObject(target); context.setCurrentType(Object.class); } catch (NullPointerException e) { throw new UnsupportedCompilationException("evaluation resulted in null expression."); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return result; } public String toSetSourceString(C context, Object target) { if (children.length != 2) throw new UnsupportedCompilationException("Can only compile boolean expressions with two children."); String pre = (String) context.get("_currentChain"); if (pre == null) pre = ""; String result = ""; try { if (!OgnlOps.booleanValue(children[0].getValue(context, target))) { throw new UnsupportedCompilationException("And expression can't be compiled until all conditions are true."); } String first = ExpressionCompiler.getRootExpression(children[0], context.getRoot(), context) + pre + children[0].toGetSourceString(context, target); children[1].getValue(context, target); String second = ExpressionCompiler.getRootExpression(children[1], context.getRoot(), context) + pre + children[1].toSetSourceString(context, target); if (!OgnlRuntime.isBoolean(first)) result += "if(ognl.OgnlOps.booleanValue(" + first + ")){"; else result += "if(" + first + "){"; result += second; result += "; } "; context.setCurrentObject(target); context.setCurrentType(Object.class); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return result; } } ================================================ FILE: ognl/src/main/java/ognl/ASTAssign.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.OrderedReturn; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTAssign> extends SimpleNode { @Serial private static final long serialVersionUID = -5122854469359392542L; public ASTAssign(int id) { super(id); } public ASTAssign(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = children[1].getValue(context, source); children[0].setValue(context, source, result); return result; } public String toString() { return children[0] + " = " + children[1]; } public String toGetSourceString(C context, Object target) { String result = ""; String first = children[0].toGetSourceString(context, target); String second = ""; if (children[1] instanceof ASTProperty) { second += "((" + OgnlRuntime.getCompiler().getClassName(target.getClass()) + ")$2)."; } second += children[1].toGetSourceString(context, target); if (ASTSequence.class.isAssignableFrom(children[1].getClass())) { ASTSequence seq = (ASTSequence) children[1]; context.setCurrentType(Object.class); String core = seq.getCoreExpression(); if (core.endsWith(";")) core = core.substring(0, core.lastIndexOf(";")); second = OgnlRuntime.getCompiler().createLocalReference(context, "ognl.OgnlOps.returnValue(($w)" + core + ", ($w) " + seq.getLastExpression() + ")", Object.class); } if (children[1] instanceof NodeType && !(children[1] instanceof ASTProperty) && ((NodeType) children[1]).getGetterClass() != null && !(children[1] instanceof OrderedReturn)) { second = "new " + ((NodeType) children[1]).getGetterClass().getName() + "(" + second + ")"; } if (OrderedReturn.class.isAssignableFrom(children[0].getClass()) && ((OrderedReturn) children[0]).getCoreExpression() != null) { context.setCurrentType(Object.class); result = first + second + ")"; result = OgnlRuntime.getCompiler().createLocalReference(context, "ognl.OgnlOps.returnValue(($w)" + result + ", ($w)" + ((OrderedReturn) children[0]).getLastExpression() + ")", Object.class); } return result; } public String toSetSourceString(C context, Object target) { String result = ""; result += children[0].toSetSourceString(context, target); if (children[1] instanceof ASTProperty) { result += "((" + OgnlRuntime.getCompiler().getClassName(target.getClass()) + ")$2)."; } String value = children[1].toSetSourceString(context, target); if (value == null) throw new UnsupportedCompilationException("Value for assignment is null, can't enhance statement to bytecode."); if (ASTSequence.class.isAssignableFrom(children[1].getClass())) { ASTSequence seq = (ASTSequence) children[1]; result = seq.getCoreExpression() + result; value = seq.getLastExpression(); } if (children[1] instanceof NodeType && !(children[1] instanceof ASTProperty) && ((NodeType) children[1]).getGetterClass() != null) { value = "new " + ((NodeType) children[1]).getGetterClass().getName() + "(" + value + ")"; } return result + value + ")"; } @Override public boolean isOperation(C context) { return true; } } ================================================ FILE: ognl/src/main/java/ognl/ASTBitAnd.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTBitAnd> extends NumericExpression { @Serial private static final long serialVersionUID = -2637835347939234142L; public ASTBitAnd(int id) { super(id); } public ASTBitAnd(OgnlParser p, int id) { super(p, id); } public void jjtClose() { flattenTree(); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = children[0].getValue(context, source); for (int i = 1; i < children.length; ++i) result = OgnlOps.binaryAnd(result, children[i].getValue(context, source)); return result; } public String getExpressionOperator(int index) { return "&"; } public String coerceToNumeric(String source, C context, Node child) { return "(long)" + super.coerceToNumeric(source, context, child); } } ================================================ FILE: ognl/src/main/java/ognl/ASTBitNegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTBitNegate> extends NumericExpression { @Serial private static final long serialVersionUID = -5446238923267167955L; public ASTBitNegate(int id) { super(id); } public ASTBitNegate(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { return OgnlOps.bitNegate(children[0].getValue(context, source)); } public String toString() { return "~" + children[0]; } public String toGetSourceString(C context, Object target) { String source = children[0].toGetSourceString(context, target); if (!(children[0] instanceof ASTBitNegate)) { return "~(" + super.coerceToNumeric(source, context, children[0]) + ")"; } else { return "~(" + source + ")"; } } } ================================================ FILE: ognl/src/main/java/ognl/ASTBitOr.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTBitOr> extends NumericExpression { @Serial private static final long serialVersionUID = -1596881192315259748L; public ASTBitOr(int id) { super(id); } public ASTBitOr(OgnlParser p, int id) { super(p, id); } public void jjtClose() { flattenTree(); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = children[0].getValue(context, source); for (int i = 1; i < children.length; ++i) result = OgnlOps.binaryOr(result, children[i].getValue(context, source)); return result; } public String getExpressionOperator(int index) { return "|"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTChain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.OrderedReturn; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.lang.reflect.Array; public class ASTChain> extends SimpleNode implements NodeType, OrderedReturn { @Serial private static final long serialVersionUID = -4684366534386585181L; private final boolean shortCircuit = Boolean.parseBoolean(System.getProperty("ognl.chain.short-circuit", "true")); private boolean nullSafe = false; private Class getterClass; private Class setterClass; private String lastExpression; private String coreExpression; public ASTChain(int id) { super(id); } public ASTChain(OgnlParser p, int id) { super(p, id); } public String getLastExpression() { return lastExpression; } public String getCoreExpression() { return coreExpression; } @Override public void jjtClose() { flattenTree(); } /** * Sets whether this chain uses null-safe navigation (?. operator). * * @param nullSafe true if this is a null-safe chain, false otherwise */ public void setNullSafe(boolean nullSafe) { this.nullSafe = nullSafe; } /** * Returns whether this chain uses null-safe navigation. * * @return true if this is a null-safe chain, false otherwise */ public boolean isNullSafe() { return nullSafe; } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = source; // null-safe operator: return null immediately if source is null if (nullSafe && result == null) { return null; } // short-circuit the chain only in case if the root is null and this isn't IN operator if (shortCircuit && result == null && !(parent instanceof ASTIn)) { return null; } for (int i = 0, ilast = children.length - 1; i <= ilast; ++i) { // null-safe operator: return null if intermediate result is null if (nullSafe && result == null) { return null; } // short-circuit the chain only in case if the root is null and accessing property if (shortCircuit && result == null && (children[i] instanceof ASTProperty)) { return null; } boolean handled = false; if (i < ilast && children[i] instanceof ASTProperty propertyNode) { int indexType = propertyNode.getIndexedPropertyType(context, result); if ((indexType != OgnlRuntime.INDEXED_PROPERTY_NONE) && (children[i + 1] instanceof ASTProperty indexNode) && indexNode.isIndexedAccess()) { Object index = indexNode.getProperty(context, result); if (index instanceof DynamicSubscript dynamicSubscript) { if (indexType == OgnlRuntime.INDEXED_PROPERTY_INT) { Object array = propertyNode.getValue(context, result); int len = Array.getLength(array); switch (dynamicSubscript.getFlag()) { case DynamicSubscript.ALL: result = Array.newInstance(array.getClass().getComponentType(), len); System.arraycopy(array, 0, result, 0, len); handled = true; i++; break; case DynamicSubscript.FIRST: index = (len > 0) ? 0 : -1; break; case DynamicSubscript.MID: index = (len > 0) ? (len / 2) : -1; break; case DynamicSubscript.LAST: index = (len > 0) ? (len - 1) : -1; break; } } else { if (indexType == OgnlRuntime.INDEXED_PROPERTY_OBJECT) { throw new OgnlException( "DynamicSubscript '" + indexNode + "' not allowed for object indexed property '" + propertyNode + "'"); } } } if (!handled) { result = OgnlRuntime.getIndexedProperty(context, result, propertyNode.getProperty(context, result).toString(), index); handled = true; i++; } } } if (!handled) { result = children[i].getValue(context, result); } } return result; } @Override protected void setValueBody(C context, Object target, Object value) throws OgnlException { boolean handled = false; for (int i = 0, ilast = children.length - 2; i <= ilast; ++i) { if (children[i] instanceof ASTProperty propertyNode) { int indexType = propertyNode.getIndexedPropertyType(context, target); if ((indexType != OgnlRuntime.INDEXED_PROPERTY_NONE) && (children[i + 1] instanceof ASTProperty indexNode)) { if (indexNode.isIndexedAccess()) { Object index = indexNode.getProperty(context, target); if (index instanceof DynamicSubscript dynamicSubscript) { if (indexType == OgnlRuntime.INDEXED_PROPERTY_INT) { Object array = propertyNode.getValue(context, target); int len = Array.getLength(array); switch (dynamicSubscript.getFlag()) { case DynamicSubscript.ALL: System.arraycopy(target, 0, value, 0, len); handled = true; i++; break; case DynamicSubscript.FIRST: index = (len > 0) ? 0 : -1; break; case DynamicSubscript.MID: index = (len > 0) ? (len / 2) : -1; break; case DynamicSubscript.LAST: index = (len > 0) ? (len - 1) : -1; break; } } else { if (indexType == OgnlRuntime.INDEXED_PROPERTY_OBJECT) { throw new OgnlException("DynamicSubscript '" + indexNode + "' not allowed for object indexed property '" + propertyNode + "'"); } } } if (!handled && i == ilast) { OgnlRuntime.setIndexedProperty(context, target, propertyNode.getProperty(context, target).toString(), index, value); handled = true; i++; } else if (!handled) { target = OgnlRuntime.getIndexedProperty(context, target, propertyNode.getProperty(context, target).toString(), index); i++; continue; } } } } if (!handled) { target = children[i].getValue(context, target); } } if (!handled) { children[children.length - 1].setValue(context, target, value); } } @Override public boolean isSimpleNavigationChain(C context) throws OgnlException { boolean result = false; if ((children != null) && (children.length > 0)) { result = true; for (int i = 0; result && (i < children.length); i++) { if (children[i] instanceof SimpleNode) { result = ((SimpleNode) children[i]).isSimpleProperty(context); } else { result = false; } } } return result; } public Class getGetterClass() { return getterClass; } public Class getSetterClass() { return setterClass; } @Override public String toString() { StringBuilder result = new StringBuilder(); if ((children != null) && (children.length > 0)) { for (int i = 0; i < children.length; i++) { if (i > 0 && shouldAppendNavigationOperator(children[i])) { result.append(nullSafe ? "?." : "."); } result.append(children[i].toString()); } } return result.toString(); } private boolean shouldAppendNavigationOperator(Node child) { return !(child instanceof ASTProperty) || !((ASTProperty) child).isIndexedAccess(); } @Override public String toGetSourceString(C context, Object target) { String prevChain = (String) context.get(OgnlContext.CURRENT_CHAIN); if (target != null) { context.setCurrentObject(target); context.setCurrentType(target.getClass()); } // For null-safe chains, wrap in null check if (nullSafe && target == null) { return "null"; } String result = ""; NodeType lastType = null; boolean ordered = false; boolean constructor = false; try { if (children != null) { for (Node child : children) { String value = child.toGetSourceString(context, context.getCurrentObject()); if (child instanceof ASTCtor) constructor = true; if (child instanceof NodeType nodeType && nodeType.getGetterClass() != null) { lastType = nodeType; } if (!(child instanceof ASTVarRef) && !constructor && !(child instanceof OrderedReturn orderedReturn && orderedReturn.getLastExpression() != null) && (!(parent instanceof ASTSequence))) { value = OgnlRuntime.getCompiler().castExpression(context, child, value); } if (child instanceof OrderedReturn or && or.getLastExpression() != null) { ordered = true; if (or.getCoreExpression() == null || or.getCoreExpression().trim().isEmpty()) result = ""; else result += or.getCoreExpression(); lastExpression = or.getLastExpression(); if (context.get(ExpressionCompiler.PRE_CAST) != null) { lastExpression = context.remove(ExpressionCompiler.PRE_CAST) + lastExpression; } } else if (child instanceof ASTOr || child instanceof ASTAnd || child instanceof ASTCtor || (child instanceof ASTStaticField && parent == null)) { context.put("_noRoot", "true"); result = value; } else { result += value; } context.put(OgnlContext.CURRENT_CHAIN, result); } } } catch (Exception t) { throw OgnlOps.castToRuntime(t); } if (lastType != null) { getterClass = lastType.getGetterClass(); setterClass = lastType.getSetterClass(); } if (ordered) { coreExpression = result; } context.put(OgnlContext.CURRENT_CHAIN, prevChain); return result; } @Override public String toSetSourceString(C context, Object target) { String prevChain = (String) context.get(OgnlContext.CURRENT_CHAIN); String prevChild = (String) context.get(OgnlContext.LAST_CHILD); if (prevChain != null) throw new UnsupportedCompilationException("Can't compile nested chain expressions."); if (target != null) { context.setCurrentObject(target); context.setCurrentType(target.getClass()); } String result = ""; NodeType lastType = null; boolean constructor = false; try { if ((children != null) && (children.length > 0)) { if (children[0] instanceof ASTConst) { throw new UnsupportedCompilationException("Can't modify constant values."); } for (int i = 0; i < children.length; i++) { if (i == (children.length - 1)) { context.put(OgnlContext.LAST_CHILD, "true"); } String value = children[i].toSetSourceString(context, context.getCurrentObject()); if (children[i] instanceof ASTCtor) constructor = true; if (children[i] instanceof NodeType nodeType && nodeType.getGetterClass() != null) { lastType = (NodeType) children[i]; } if (!(children[i] instanceof ASTVarRef) && !constructor && !(children[i] instanceof OrderedReturn orderedReturn && orderedReturn.getLastExpression() != null) && (!(parent instanceof ASTSequence))) { value = OgnlRuntime.getCompiler().castExpression(context, children[i], value); } if (children[i] instanceof ASTOr || children[i] instanceof ASTAnd || children[i] instanceof ASTCtor || children[i] instanceof ASTStaticField) { context.put("_noRoot", "true"); result = value; } else result += value; context.put(OgnlContext.CURRENT_CHAIN, result); } } } catch (Exception t) { throw OgnlOps.castToRuntime(t); } context.put(OgnlContext.LAST_CHILD, prevChild); context.put(OgnlContext.CURRENT_CHAIN, prevChain); if (lastType != null) setterClass = lastType.getSetterClass(); return result; } @Override public boolean isChain(C context) { return true; } } ================================================ FILE: ognl/src/main/java/ognl/ASTConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.math.BigDecimal; import java.math.BigInteger; public class ASTConst> extends SimpleNode implements NodeType { @Serial private static final long serialVersionUID = -2322429350041196376L; private Object value; private Class getterClass; public ASTConst(int id) { super(id); } public ASTConst(OgnlParser p, int id) { super(p, id); } /** * Called from parser actions. * * @param value the Object representing the value. */ public void setValue(Object value) { this.value = value; } public Object getValue() { return value; } protected Object getValueBody(C context, Object source) throws OgnlException { return this.value; } public boolean isNodeConstant(C context) throws OgnlException { return true; } public Class getGetterClass() { if (getterClass == null) return null; return getterClass; } public Class getSetterClass() { return null; } public String toString() { String result; if (value == null) { result = "null"; } else { if (value instanceof String) { result = '\"' + OgnlOps.getEscapeString(value.toString()) + '\"'; } else { if (value instanceof Character) { result = '\'' + OgnlOps.getEscapedChar((Character) value) + '\''; } else { result = value.toString(); if (value instanceof Long) { result = result + "L"; } else { if (value instanceof BigDecimal) { result = result + "B"; } else { if (value instanceof BigInteger) { result = result + "H"; } else { if (value instanceof Node) { result = ":[ " + result + " ]"; } } } } } } } return result; } public String toGetSourceString(C context, Object target) { if (value == null && parent instanceof ExpressionNode) { context.setCurrentType(null); return "null"; } else if (value == null) { context.setCurrentType(null); return ""; } getterClass = value.getClass(); Object retval; if (parent instanceof ASTProperty) { context.setCurrentObject(value); return value.toString(); } else if (Number.class.isAssignableFrom(value.getClass())) { context.setCurrentType(OgnlRuntime.getPrimitiveWrapperClass(value.getClass())); context.setCurrentObject(value); String result = value.toString(); if (value instanceof Long) { result = result + "L"; } else if (value instanceof Float) { result = result + "f"; } return result; } else if (!(parent != null && NumericExpression.class.isAssignableFrom(parent.getClass())) && String.class.isAssignableFrom(value.getClass())) { context.setCurrentType(String.class); retval = '\"' + OgnlOps.getEscapeString(value.toString()) + '\"'; context.setCurrentObject(retval.toString()); return retval.toString(); } else if (value instanceof Character) { Character val = (Character) value; context.setCurrentType(Character.class); if (Character.isLetterOrDigit(val)) retval = "'" + value + "'"; else retval = "'" + OgnlOps.getEscapedChar((Character) value) + "'"; context.setCurrentObject(retval); return retval.toString(); } if (Boolean.class.isAssignableFrom(value.getClass())) { getterClass = Boolean.TYPE; context.setCurrentType(Boolean.TYPE); context.setCurrentObject(value); return value.toString(); } return value.toString(); } public String toSetSourceString(C context, Object target) { if (parent == null) throw new UnsupportedCompilationException("Can't modify constant values."); return toGetSourceString(context, target); } } ================================================ FILE: ognl/src/main/java/ognl/ASTCtor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import java.io.Serial; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.util.List; public class ASTCtor> extends SimpleNode { @Serial private static final long serialVersionUID = 1729160437205622304L; private String className; private boolean isArray; public ASTCtor(int id) { super(id); } public ASTCtor(OgnlParser p, int id) { super(p, id); } /** * Called from parser action. */ void setClassName(String className) { this.className = className; } Class getCreatedClass(C context) throws ClassNotFoundException { return OgnlRuntime.classForName(context, className); } void setArray(boolean value) { isArray = value; } public boolean isArray() { return isArray; } protected Object getValueBody(C context, Object source) throws OgnlException { Object result, root = context.getRoot(); int count = jjtGetNumChildren(); Object[] args = new Object[count]; for (int i = 0; i < count; ++i) { args[i] = children[i].getValue(context, root); } if (isArray) { if (args.length == 1) { try { Class componentClass = OgnlRuntime.classForName(context, className); List sourceList = null; int size; if (args[0] instanceof List) { sourceList = (List) args[0]; size = sourceList.size(); } else { size = (int) OgnlOps.longValue(args[0]); } result = Array.newInstance(componentClass, size); if (sourceList != null) { TypeConverter converter = context.getTypeConverter(); for (int i = 0, icount = sourceList.size(); i < icount; i++) { Object o = sourceList.get(i); if ((o == null) || componentClass.isInstance(o)) { Array.set(result, i, o); } else { Array.set(result, i, converter.convertValue(context, null, null, null, o, componentClass)); } } } } catch (ClassNotFoundException ex) { throw new OgnlException("array component class '" + className + "' not found", ex); } } else { throw new OgnlException("only expect array size or fixed initializer list"); } } else { result = OgnlRuntime.callConstructor(context, className, args); } return result; } public String toString() { StringBuilder result = new StringBuilder("new " + className); if (isArray) { if (children[0] instanceof ASTConst) { result.append("[").append(children[0]).append("]"); } else { result.append("[] ").append(children[0]); } } else { result.append("("); if ((children != null) && (children.length > 0)) { for (int i = 0; i < children.length; i++) { if (i > 0) { result.append(", "); } result.append(children[i]); } } result.append(")"); } return result.toString(); } public String toGetSourceString(C context, Object target) { StringBuilder result = new StringBuilder("new " + className); Class clazz; Object ctorValue; try { clazz = OgnlRuntime.classForName(context, className); ctorValue = this.getValueBody(context, target); context.setCurrentObject(ctorValue); if (ctorValue != null) { context.setCurrentType(ctorValue.getClass()); context.setCurrentAccessor(ctorValue.getClass()); } if (isArray) context.put("_ctorClass", clazz); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } try { if (isArray) { if (children[0] instanceof ASTConst) { result.append("[").append(children[0].toGetSourceString(context, target)).append("]"); } else if (children[0] instanceof ASTProperty) { result.append("[").append(ExpressionCompiler.getRootExpression(children[0], target, context)).append(children[0].toGetSourceString(context, target)).append("]"); } else if (children[0] instanceof ASTChain) { result.append("[").append(children[0].toGetSourceString(context, target)).append("]"); } else { result.append("[] ").append(children[0].toGetSourceString(context, target)); } } else { result.append("("); if ((children != null) && (children.length > 0)) { Object[] values = new Object[children.length]; String[] expressions = new String[children.length]; Class[] types = new Class[children.length]; // first populate arrays with child values for (int i = 0; i < children.length; i++) { Object objValue = children[i].getValue(context, context.getRoot()); String value = children[i].toGetSourceString(context, target); if (!(children[i] instanceof ASTRootVarRef)) { value = ExpressionCompiler.getRootExpression(children[i], target, context) + value; } String cast = ""; if (ExpressionCompiler.shouldCast(children[i])) { cast = (String) context.remove(ExpressionCompiler.PRE_CAST); } if (cast == null) cast = ""; if (!(children[i] instanceof ASTConst)) value = cast + value; values[i] = objValue; expressions[i] = value; types[i] = context.getCurrentType(); } // now try and find a matching constructor Constructor[] cons = clazz.getConstructors(); Constructor ctor = null; Class[] ctorParamTypes = null; for (Constructor con : cons) { Class[] ctorTypes = con.getParameterTypes(); if (OgnlRuntime.areArgsCompatible(values, ctorTypes) && (ctor == null || OgnlRuntime.isMoreSpecific(ctorTypes, ctorParamTypes))) { ctor = con; ctorParamTypes = ctorTypes; } } if (ctor == null) ctor = OgnlRuntime.getConvertedConstructorAndArgs(context, clazz, OgnlRuntime.getConstructors(clazz), values, new Object[values.length]); if (ctor == null) throw new NoSuchMethodException("Unable to find constructor appropriate for arguments in class: " + clazz); ctorParamTypes = ctor.getParameterTypes(); // now loop over child values again and build up the actual source string for (int i = 0; i < children.length; i++) { if (i > 0) { result.append(", "); } String value = expressions[i]; if (types[i].isPrimitive()) { String literal = OgnlRuntime.getNumericLiteral(types[i]); if (literal != null) value += literal; } if (ctorParamTypes[i] != types[i]) { if (values[i] != null && !types[i].isPrimitive() && !values[i].getClass().isArray() && !(children[i] instanceof ASTConst)) { value = "(" + OgnlRuntime.getCompiler().getInterfaceClass(values[i].getClass()).getName() + ")" + value; } else if (!(children[i] instanceof ASTConst) || (children[i] instanceof ASTConst && !types[i].isPrimitive())) { if (!types[i].isArray() && types[i].isPrimitive() && !ctorParamTypes[i].isPrimitive()) value = "new " + ExpressionCompiler.getCastString(OgnlRuntime.getPrimitiveWrapperClass(types[i])) + "(" + value + ")"; else value = " ($w) " + value; } } result.append(value); } } result.append(")"); } context.setCurrentType(ctorValue != null ? ctorValue.getClass() : clazz); context.setCurrentAccessor(clazz); context.setCurrentObject(ctorValue); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } context.remove("_ctorClass"); return result.toString(); } public String toSetSourceString(C context, Object target) { return ""; } } ================================================ FILE: ognl/src/main/java/ognl/ASTDivide.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTDivide> extends NumericExpression { @Serial private static final long serialVersionUID = 7924993880701535061L; public ASTDivide(int id) { super(id); } public ASTDivide(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.divide(v1, v2); } public String getExpressionOperator(int index) { return "/"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTEq.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTEq> extends ComparisonExpression { @Serial private static final long serialVersionUID = 2327248469141797794L; public ASTEq(int id) { super(id); } public ASTEq(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.equal(v1, v2) ? Boolean.TRUE : Boolean.FALSE; } public String getExpressionOperator(int index) { return "=="; } public String getComparisonFunction() { return "ognl.OgnlOps.equal"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTEval.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTEval> extends SimpleNode { @Serial private static final long serialVersionUID = 1664536168007480363L; public ASTEval(int id) { super(id); } public ASTEval(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result, expr = children[0].getValue(context, source), previousRoot = context.getRoot(); source = children[1].getValue(context, source); @SuppressWarnings("unchecked") Node node = (expr instanceof Node) ? (Node) expr : (Node) Ognl.parseExpression(expr.toString()); try { context.setRoot(source); result = node.getValue(context, source); } finally { context.setRoot(previousRoot); } return result; } protected void setValueBody(C context, Object target, Object value) throws OgnlException { Object expr = children[0].getValue(context, target), previousRoot = context.getRoot(); target = children[1].getValue(context, target); @SuppressWarnings("unchecked") Node node = (expr instanceof Node) ? (Node) expr : (Node) Ognl.parseExpression(expr.toString()); try { context.setRoot(target); node.setValue(context, target, value); } finally { context.setRoot(previousRoot); } } @Override public boolean isEvalChain(C context) throws OgnlException { return true; } public String toString() { return "(" + children[0] + ")(" + children[1] + ")"; } public String toGetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Eval expressions not supported as native java yet."); } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Map expressions not supported as native java yet."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTGreater.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTGreater> extends ComparisonExpression { @Serial private static final long serialVersionUID = -8158173472092839867L; public ASTGreater(int id) { super(id); } public ASTGreater(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.greater(v1, v2) ? Boolean.TRUE : Boolean.FALSE; } public String getExpressionOperator(int index) { return ">"; } public String getComparisonFunction() { return "ognl.OgnlOps.greater"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTGreaterEq.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTGreaterEq> extends ComparisonExpression { @Serial private static final long serialVersionUID = -5165257674133751294L; public ASTGreaterEq(int id) { super(id); } public ASTGreaterEq(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.less(v1, v2) ? Boolean.FALSE : Boolean.TRUE; } public String getExpressionOperator(int index) { return ">="; } public String getComparisonFunction() { return "!ognl.OgnlOps.less"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTIn.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTIn> extends SimpleNode implements NodeType { @Serial private static final long serialVersionUID = 7578881819156316646L; public ASTIn(int id) { super(id); } public ASTIn(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.in(v1, v2) ? Boolean.TRUE : Boolean.FALSE; } public String toString() { return children[0] + " in " + children[1]; } public Class getGetterClass() { return Boolean.TYPE; } public Class getSetterClass() { return null; } public String toGetSourceString(C context, Object target) { try { String result = "ognl.OgnlOps.in( ($w) "; result += OgnlRuntime.getChildSource(context, target, children[0]) + ", ($w) " + OgnlRuntime.getChildSource(context, target, children[1]); result += ")"; context.setCurrentType(Boolean.TYPE); return result; } catch (NullPointerException e) { // expected to happen in some instances throw new UnsupportedCompilationException("Evaluation resulted in null expression.", e); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Map expressions not supported as native java yet."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTInstanceof.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTInstanceof> extends SimpleNode implements NodeType { @Serial private static final long serialVersionUID = -3678795634846405975L; private String targetType; public ASTInstanceof(int id) { super(id); } public ASTInstanceof(OgnlParser p, int id) { super(p, id); } void setTargetType(String targetType) { this.targetType = targetType; } protected Object getValueBody(C context, Object source) throws OgnlException { Object value = children[0].getValue(context, source); return OgnlRuntime.isInstance(context, value, targetType) ? Boolean.TRUE : Boolean.FALSE; } public String toString() { return children[0] + " instanceof " + targetType; } public Class getGetterClass() { return boolean.class; } public Class getSetterClass() { return null; } public String toGetSourceString(C context, Object target) { try { String ret; if (children[0] instanceof ASTConst) ret = ((Boolean) getValueBody(context, target)).toString(); else ret = children[0].toGetSourceString(context, target) + " instanceof " + targetType; context.setCurrentType(Boolean.TYPE); context.put("_noRoot", "true"); return ret; } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } public String toSetSourceString(C context, Object target) { return toGetSourceString(context, target); } } ================================================ FILE: ognl/src/main/java/ognl/ASTKeyValue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTKeyValue> extends SimpleNode { @Serial private static final long serialVersionUID = 5035649093189318943L; public ASTKeyValue(int id) { super(id); } public ASTKeyValue(OgnlParser p, int id) { super(p, id); } protected Node getKey() { return children[0]; } protected Node getValue() { return (jjtGetNumChildren() > 1) ? children[1] : null; } /** * Returns null because this is a parser construct and does not evaluate */ protected Object getValueBody(C context, Object source) throws OgnlException { return null; } public String toString() { return getKey() + " -> " + getValue(); } } ================================================ FILE: ognl/src/main/java/ognl/ASTLess.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTLess> extends ComparisonExpression { @Serial private static final long serialVersionUID = -5575803930862133743L; public ASTLess(int id) { super(id); } public ASTLess(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.less(v1, v2) ? Boolean.TRUE : Boolean.FALSE; } public String getExpressionOperator(int index) { return "<"; } public String getComparisonFunction() { return "ognl.OgnlOps.less"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTLessEq.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTLessEq> extends ComparisonExpression { @Serial private static final long serialVersionUID = 1564876674732452015L; public ASTLessEq(int id) { super(id); } public ASTLessEq(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.greater(v1, v2) ? Boolean.FALSE : Boolean.TRUE; } public String getExpressionOperator(int index) { return "<="; } public String getComparisonFunction() { return "!ognl.OgnlOps.greater"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTList.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class ASTList> extends SimpleNode implements NodeType { @Serial private static final long serialVersionUID = 6663713724275707392L; public ASTList(int id) { super(id); } public ASTList(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { List answer = new ArrayList<>(jjtGetNumChildren()); for (int i = 0; i < jjtGetNumChildren(); ++i) { answer.add(children[i].getValue(context, source)); } return answer; } public Class getGetterClass() { return null; } public Class getSetterClass() { return null; } public String toString() { StringBuilder result = new StringBuilder("{ "); for (int i = 0; i < jjtGetNumChildren(); ++i) { if (i > 0) { result.append(", "); } result.append(children[i].toString()); } return result + " }"; } public String toGetSourceString(C context, Object target) { StringBuilder result = new StringBuilder(); boolean array = parent instanceof ASTCtor && ((ASTCtor) parent).isArray(); context.setCurrentType(List.class); context.setCurrentAccessor(List.class); if (!array) { if (jjtGetNumChildren() < 1) return "java.util.Arrays.asList( new Object[0])"; result.append("java.util.Arrays.asList( new Object[] "); } result.append("{ "); try { for (int i = 0; i < jjtGetNumChildren(); ++i) { if (i > 0) { result.append(", "); } Class prevType = context.getCurrentType(); Object objValue = children[i].getValue(context, context.getRoot()); String value = children[i].toGetSourceString(context, target); // to undo type setting of constants when used as method parameters if (children[i] instanceof ASTConst) { context.setCurrentType(prevType); } value = ExpressionCompiler.getRootExpression(children[i], target, context) + value; String cast = ""; if (ExpressionCompiler.shouldCast(children[i])) { cast = (String) context.remove(ExpressionCompiler.PRE_CAST); } if (cast == null) cast = ""; if (!(children[i] instanceof ASTConst)) value = cast + value; Class ctorClass = (Class) context.get("_ctorClass"); if (array && ctorClass != null && !ctorClass.isPrimitive()) { Class valueClass = value.getClass(); if (NodeType.class.isAssignableFrom(children[i].getClass())) valueClass = ((NodeType) children[i]).getGetterClass(); if (valueClass != null && ctorClass.isArray()) { value = OgnlRuntime.getCompiler().createLocalReference(context, "(" + ExpressionCompiler.getCastString(ctorClass) + ")ognl.OgnlOps.toArray(" + value + ", " + ctorClass.getComponentType().getName() + ".class, true)", ctorClass ); } else if (ctorClass != Object.class) { value = OgnlRuntime.getCompiler().createLocalReference(context, "(" + ctorClass.getName() + ")ognl.OgnlOps.convertValue(" + value + "," + ctorClass.getName() + ".class)", ctorClass ); } else if ((children[i] instanceof NodeType && ((NodeType) children[i]).getGetterClass() != null && Number.class.isAssignableFrom(((NodeType) children[i]).getGetterClass())) || Objects.requireNonNull(valueClass).isPrimitive()) { value = " ($w) (" + value + ")"; } } else if (ctorClass == null || !ctorClass.isPrimitive()) { value = " ($w) (" + value + ")"; } if (objValue == null || value.length() <= 0) value = "null"; result.append(value); } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } context.setCurrentType(List.class); context.setCurrentAccessor(List.class); result.append("}"); if (!array) result.append(")"); return result.toString(); } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Can't generate setter for ASTList."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTMap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.util.LinkedHashMap; import java.util.Map; public class ASTMap> extends SimpleNode { @Serial private static final long serialVersionUID = -7919578192338088973L; private String className; public ASTMap(int id) { super(id); } public ASTMap(OgnlParser p, int id) { super(p, id); } protected void setClassName(String value) { className = value; } protected Object getValueBody(C context, Object source) throws OgnlException { Map answer; if (className == null) { answer = new LinkedHashMap<>(); } else { try { answer = (Map) OgnlRuntime.classForName(context, className).newInstance(); } catch (Exception ex) { throw new OgnlException("Map implementor '" + className + "' not found", ex); } } for (int i = 0; i < jjtGetNumChildren(); ++i) { ASTKeyValue kv = (ASTKeyValue) children[i]; Node k = kv.getKey(), v = kv.getValue(); answer.put(k.getValue(context, source), (v == null) ? null : v.getValue(context, source)); } return answer; } public String toString() { StringBuilder result = new StringBuilder("#"); if (className != null) { result.append("@").append(className).append("@"); } result.append("{ "); for (int i = 0; i < jjtGetNumChildren(); ++i) { ASTKeyValue kv = (ASTKeyValue) children[i]; if (i > 0) { result.append(", "); } result.append(kv.getKey()).append(" : ").append(kv.getValue()); } return result + " }"; } public String toGetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Map expressions not supported as native java yet."); } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Map expressions not supported as native java yet."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTMethod.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.OrderedReturn; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.lang.reflect.Method; import java.util.List; public class ASTMethod> extends SimpleNode implements OrderedReturn, NodeType { @Serial private static final long serialVersionUID = 312574881386865796L; private String methodName; private String lastExpression; private String coreExpression; private Class getterClass; public ASTMethod(int id) { super(id); } public ASTMethod(OgnlParser p, int id) { super(p, id); } /** * Called from parser action. * * @param methodName the method name. */ public void setMethodName(String methodName) { this.methodName = methodName; } /** * Returns the method name that this node will call. * * @return the method name. */ public String getMethodName() { return methodName; } protected Object getValueBody(C context, Object source) throws OgnlException { Object[] args = new Object[jjtGetNumChildren()]; Object result, root = context.getRoot(); for (int i = 0, icount = args.length; i < icount; ++i) { args[i] = children[i].getValue(context, root); } result = OgnlRuntime.callMethod(context, source, methodName, args); if (result == null) { NullHandler nh = OgnlRuntime.getNullHandler(OgnlRuntime.getTargetClass(source)); result = nh.nullMethodResult(context, source, methodName, args); } return result; } public String getLastExpression() { return lastExpression; } public String getCoreExpression() { return coreExpression; } public Class getGetterClass() { return getterClass; } public Class getSetterClass() { return getterClass; } public String toString() { StringBuilder result = new StringBuilder(methodName); result.append("("); if ((children != null) && (children.length > 0)) { for (int i = 0; i < children.length; i++) { if (i > 0) { result.append(", "); } result.append(children[i]); } } result.append(")"); return result.toString(); } public String toGetSourceString(C context, Object target) { if (target == null) { throw new UnsupportedCompilationException("Target object is null."); } String post = ""; StringBuilder result; Method m; try { m = OgnlRuntime.getMethod(context, context.getCurrentType() != null ? context.getCurrentType() : target.getClass(), methodName, children, false); Class[] argumentClasses = getChildrenClasses(context, children); if (m == null) m = OgnlRuntime.getReadMethod(target.getClass(), methodName, argumentClasses); if (m == null) { m = OgnlRuntime.getWriteMethod(target.getClass(), methodName, argumentClasses); if (m != null) { context.setCurrentType(m.getReturnType()); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); coreExpression = toSetSourceString(context, target); if (coreExpression == null || coreExpression.isEmpty()) throw new UnsupportedCompilationException("can't find suitable getter method"); coreExpression += ";"; lastExpression = "null"; return coreExpression; } return ""; } else { getterClass = m.getReturnType(); } // TODO: This is a hacky workaround until javassist supports varargs method invocations boolean varArgs = m.isVarArgs(); if (varArgs) { throw new UnsupportedCompilationException("Javassist does not currently support varargs method calls"); } result = new StringBuilder("." + m.getName() + "("); if ((children != null) && (children.length > 0)) { Class[] parms = m.getParameterTypes(); String prevCast = (String) context.remove(ExpressionCompiler.PRE_CAST); for (int i = 0; i < children.length; i++) { if (i > 0) { result.append(", "); } Class prevType = context.getCurrentType(); context.setCurrentObject(context.getRoot()); context.setCurrentType(context.getRoot() != null ? context.getRoot().getClass() : null); context.setCurrentAccessor(null); context.setPreviousType(null); Object value = children[i].getValue(context, context.getRoot()); String parmString = children[i].toGetSourceString(context, context.getRoot()); if (parmString == null || parmString.trim().isEmpty()) parmString = "null"; // to undo type setting of constants when used as method parameters if (children[i] instanceof ASTConst) { context.setCurrentType(prevType); } parmString = ExpressionCompiler.getRootExpression(children[i], context.getRoot(), context) + parmString; String cast = ""; if (ExpressionCompiler.shouldCast(children[i])) { cast = (String) context.remove(ExpressionCompiler.PRE_CAST); } if (cast == null) cast = ""; if (!(children[i] instanceof ASTConst)) parmString = cast + parmString; Class valueClass = value != null ? value.getClass() : null; if (NodeType.class.isAssignableFrom(children[i].getClass())) valueClass = ((NodeType) children[i]).getGetterClass(); if (valueClass != parms[i]) { if (parms[i].isArray()) { parmString = OgnlRuntime.getCompiler().createLocalReference(context, "(" + ExpressionCompiler.getCastString(parms[i]) + ")ognl.OgnlOps#toArray(" + parmString + ", " + parms[i].getComponentType().getName() + ".class, true)", parms[i] ); } else if (parms[i].isPrimitive()) { Class wrapClass = OgnlRuntime.getPrimitiveWrapperClass(parms[i]); parmString = OgnlRuntime.getCompiler().createLocalReference(context, "((" + wrapClass.getName() + ")ognl.OgnlOps#convertValue(" + parmString + "," + wrapClass.getName() + ".class, true))." + OgnlRuntime.getNumericValueGetter(wrapClass), parms[i] ); } else if (parms[i] != Object.class) { parmString = OgnlRuntime.getCompiler().createLocalReference(context, "(" + parms[i].getName() + ")ognl.OgnlOps#convertValue(" + parmString + "," + parms[i].getName() + ".class)", parms[i] ); } else if ((children[i] instanceof NodeType && ((NodeType) children[i]).getGetterClass() != null && Number.class.isAssignableFrom(((NodeType) children[i]).getGetterClass())) || (valueClass != null && valueClass.isPrimitive())) { parmString = " ($w) " + parmString; } } result.append(parmString); } if (prevCast != null) { context.put(ExpressionCompiler.PRE_CAST, prevCast); } } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } try { Object contextObj = getValueBody(context, target); context.setCurrentObject(contextObj); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } result.append(")").append(post); if (m.getReturnType() == void.class) { coreExpression = result + ";"; lastExpression = "null"; } context.setCurrentType(m.getReturnType()); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); return result.toString(); } public String toSetSourceString(C context, Object target) { Method m = OgnlRuntime.getWriteMethod(context.getCurrentType() != null ? context.getCurrentType() : target.getClass(), methodName, getChildrenClasses(context, children)); if (m == null) { throw new UnsupportedCompilationException("Unable to determine setter method generation for " + methodName); } String post = ""; StringBuilder result = new StringBuilder("." + m.getName() + "("); if (m.getReturnType() != void.class && m.getReturnType().isPrimitive() && (!(parent instanceof ASTTest))) { Class wrapper = OgnlRuntime.getPrimitiveWrapperClass(m.getReturnType()); ExpressionCompiler.addCastString(context, "new " + wrapper.getName() + "("); post = ")"; getterClass = wrapper; } boolean varArgs = m.isVarArgs(); if (varArgs) { throw new UnsupportedCompilationException("Javassist does not currently support varargs method calls"); } try { if ((children != null) && (children.length > 0)) { Class[] parms = m.getParameterTypes(); String prevCast = (String) context.remove(ExpressionCompiler.PRE_CAST); for (int i = 0; i < children.length; i++) { if (i > 0) { result.append(", "); } Class prevType = context.getCurrentType(); context.setCurrentObject(context.getRoot()); context.setCurrentType(context.getRoot() != null ? context.getRoot().getClass() : null); context.setCurrentAccessor(null); context.setPreviousType(null); Object value = children[i].getValue(context, context.getRoot()); String parmString = children[i].toSetSourceString(context, context.getRoot()); if (context.getCurrentType() == Void.TYPE || context.getCurrentType() == void.class) throw new UnsupportedCompilationException("Method argument can't be a void type."); if (parmString == null || parmString.trim().isEmpty()) { if (children[i] instanceof ASTProperty || children[i] instanceof ASTMethod || children[i] instanceof ASTStaticMethod || children[i] instanceof ASTChain) throw new UnsupportedCompilationException("ASTMethod setter child returned null from a sub property expression."); parmString = "null"; } // to undo type setting of constants when used as method parameters if (children[i] instanceof ASTConst) { context.setCurrentType(prevType); } parmString = ExpressionCompiler.getRootExpression(children[i], context.getRoot(), context) + parmString; String cast = ""; if (ExpressionCompiler.shouldCast(children[i])) { cast = (String) context.remove(ExpressionCompiler.PRE_CAST); } if (cast == null) cast = ""; parmString = cast + parmString; Class valueClass = value != null ? value.getClass() : null; if (NodeType.class.isAssignableFrom(children[i].getClass())) valueClass = ((NodeType) children[i]).getGetterClass(); if (valueClass != parms[i]) { if (parms[i].isArray()) { parmString = OgnlRuntime.getCompiler().createLocalReference(context, "(" + ExpressionCompiler.getCastString(parms[i]) + ")ognl.OgnlOps#toArray(" + parmString + ", " + parms[i].getComponentType().getName() + ".class)", parms[i] ); } else if (parms[i].isPrimitive()) { Class wrapClass = OgnlRuntime.getPrimitiveWrapperClass(parms[i]); parmString = OgnlRuntime.getCompiler().createLocalReference(context, "((" + wrapClass.getName() + ")ognl.OgnlOps#convertValue(" + parmString + "," + wrapClass.getName() + ".class, true))." + OgnlRuntime.getNumericValueGetter(wrapClass), parms[i] ); } else if (parms[i] != Object.class) { parmString = OgnlRuntime.getCompiler().createLocalReference(context, "(" + parms[i].getName() + ")ognl.OgnlOps#convertValue(" + parmString + "," + parms[i].getName() + ".class)", parms[i] ); } else if ((children[i] instanceof NodeType && ((NodeType) children[i]).getGetterClass() != null && Number.class.isAssignableFrom(((NodeType) children[i]).getGetterClass())) || (valueClass != null && valueClass.isPrimitive())) { parmString = " ($w) " + parmString; } } result.append(parmString); } if (prevCast != null) { context.put(ExpressionCompiler.PRE_CAST, prevCast); } } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } try { Object contextObj = getValueBody(context, target); context.setCurrentObject(contextObj); } catch (Throwable t) { // ignore } context.setCurrentType(m.getReturnType()); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); return result + ")" + post; } private Class getClassMatchingAllChildren(C context, Node[] _children) { Class[] cc = getChildrenClasses(context, _children); Class componentType = null; for (Class ic : cc) { if (ic == null) { componentType = Object.class; // fall back to object... break; } else { if (componentType == null) { componentType = ic; } else { if (!componentType.isAssignableFrom(ic)) { if (ic.isAssignableFrom(componentType)) { componentType = ic; // just swap... ic is more generic... } else { Class pc; while ((pc = componentType.getSuperclass()) != null) { // TODO hmm - it could also be that an interface matches... if (pc.isAssignableFrom(ic)) { componentType = pc; // use this matching parent class break; } } if (!componentType.isAssignableFrom(ic)) { // parents didn't match. the types might be primitives. Fall back to object. componentType = Object.class; break; } } } } } } if (componentType == null) componentType = Object.class; return componentType; } private Class[] getChildrenClasses(C context, Node[] _children) { if (_children == null) { return null; } Class[] argumentClasses = new Class[_children.length]; for (int i = 0; i < _children.length; i++) { Node child = _children[i]; if (child instanceof ASTList) { // special handling for ASTList - it creates a List argumentClasses[i] = List.class; } else if (child instanceof NodeType) { argumentClasses[i] = ((NodeType) child).getGetterClass(); } else if (child instanceof ASTCtor) { try { argumentClasses[i] = ((ASTCtor) child).getCreatedClass(context); } catch (ClassNotFoundException nfe) { throw OgnlOps.castToRuntime(nfe); } } else if (child instanceof ASTTest) { argumentClasses[i] = getClassMatchingAllChildren(context, ((ASTTest) child).children); } else { throw new UnsupportedOperationException("Don't know how to handle child: " + child); } } return argumentClasses; } @Override public boolean isSimpleMethod(C context) { return true; } } ================================================ FILE: ognl/src/main/java/ognl/ASTMultiply.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTMultiply> extends NumericExpression { @Serial private static final long serialVersionUID = -3802338162124095297L; public ASTMultiply(int id) { super(id); } public ASTMultiply(OgnlParser p, int id) { super(p, id); } public void jjtClose() { flattenTree(); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = children[0].getValue(context, source); for (int i = 1; i < children.length; ++i) result = OgnlOps.multiply(result, children[i].getValue(context, source)); return result; } public String getExpressionOperator(int index) { return "*"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTNegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTNegate> extends NumericExpression { @Serial private static final long serialVersionUID = 4709735174162170536L; public ASTNegate(int id) { super(id); } public ASTNegate(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { return OgnlOps.negate(children[0].getValue(context, source)); } public String toString() { return "-" + children[0]; } public String toGetSourceString(C context, Object target) { String source = children[0].toGetSourceString(context, target); if (!(children[0] instanceof ASTNegate)) { return "-" + source; } else { return "-(" + source + ")"; } } @Override public boolean isOperation(C context) throws OgnlException { if (children.length == 1) { SimpleNode child = (SimpleNode) children[0]; return child.isOperation(context) || !child.isConstant(context); } return super.isOperation(context); } } ================================================ FILE: ognl/src/main/java/ognl/ASTNot.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTNot> extends BooleanExpression { @Serial private static final long serialVersionUID = 1202881695483879532L; public ASTNot(int id) { super(id); } public ASTNot(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { return OgnlOps.booleanValue(children[0].getValue(context, source)) ? Boolean.FALSE : Boolean.TRUE; } public String getExpressionOperator(int index) { return "!"; } public String toGetSourceString(C context, Object target) { try { String srcString = super.toGetSourceString(context, target); if (srcString == null || srcString.trim().isEmpty()) { srcString = "null"; } context.setCurrentType(Boolean.TYPE); return "(! ognl.OgnlOps.booleanValue(" + srcString + ") )"; } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } } ================================================ FILE: ognl/src/main/java/ognl/ASTNotEq.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTNotEq> extends ComparisonExpression { @Serial private static final long serialVersionUID = -9161185502968425136L; public ASTNotEq(int id) { super(id); } public ASTNotEq(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.equal(v1, v2) ? Boolean.FALSE : Boolean.TRUE; } public String getExpressionOperator(int index) { return "!="; } public String getComparisonFunction() { return "!ognl.OgnlOps.equal"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTNotIn.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTNotIn> extends SimpleNode implements NodeType { @Serial private static final long serialVersionUID = -1418121526223546492L; public ASTNotIn(int id) { super(id); } public ASTNotIn(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.in(v1, v2) ? Boolean.FALSE : Boolean.TRUE; } public String toString() { return children[0] + " not in " + children[1]; } public Class getGetterClass() { return Boolean.TYPE; } public Class getSetterClass() { return null; } public String toGetSourceString(C context, Object target) { try { String result = "(! ognl.OgnlOps.in( ($w) "; result += OgnlRuntime.getChildSource(context, target, children[0]) + ", ($w) " + OgnlRuntime.getChildSource(context, target, children[1]); result += ") )"; context.setCurrentType(Boolean.TYPE); return result; } catch (NullPointerException e) { // expected to happen in some instances throw new UnsupportedCompilationException("Evaluation resulted in null expression.", e); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } } ================================================ FILE: ognl/src/main/java/ognl/ASTOr.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTOr> extends BooleanExpression { @Serial private static final long serialVersionUID = 1218283928242795110L; public ASTOr(int id) { super(id); } public ASTOr(OgnlParser p, int id) { super(p, id); } public void jjtClose() { flattenTree(); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = null; int last = children.length - 1; for (int i = 0; i <= last; ++i) { result = children[i].getValue(context, source); if (i != last && OgnlOps.booleanValue(result)) break; } return result; } protected void setValueBody(C context, Object target, Object value) throws OgnlException { int last = children.length - 1; for (int i = 0; i < last; ++i) { Object v = children[i].getValue(context, target); if (OgnlOps.booleanValue(v)) return; } children[last].setValue(context, target, value); } public String getExpressionOperator(int index) { return "||"; } public Class getGetterClass() { return null; } public String toGetSourceString(C context, Object target) { if (children.length != 2) throw new UnsupportedCompilationException("Can only compile boolean expressions with two children."); String result = "("; try { String first = OgnlRuntime.getChildSource(context, target, children[0]); if (!OgnlRuntime.isBoolean(first)) first = OgnlRuntime.getCompiler().createLocalReference(context, first, context.getCurrentType()); String second = OgnlRuntime.getChildSource(context, target, children[1]); if (!OgnlRuntime.isBoolean(second)) second = OgnlRuntime.getCompiler().createLocalReference(context, second, context.getCurrentType()); result += "ognl.OgnlOps.booleanValue(" + first + ")"; result += " ? "; result += " ($w) (" + first + ")"; result += " : "; result += " ($w) (" + second + ")"; result += ")"; context.setCurrentObject(target); context.setCurrentType(Boolean.TYPE); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return result; } public String toSetSourceString(C context, Object target) { if (children.length != 2) throw new UnsupportedCompilationException("Can only compile boolean expressions with two children."); String pre = (String) context.get("_currentChain"); if (pre == null) pre = ""; String result = ""; try { children[0].getValue(context, target); String first = ExpressionCompiler.getRootExpression(children[0], context.getRoot(), context) + pre + children[0].toGetSourceString(context, target); if (!OgnlRuntime.isBoolean(first)) first = OgnlRuntime.getCompiler().createLocalReference(context, first, Object.class); children[1].getValue(context, target); String second = ExpressionCompiler.getRootExpression(children[1], context.getRoot(), context) + pre + children[1].toSetSourceString(context, target); if (!OgnlRuntime.isBoolean(second)) second = OgnlRuntime.getCompiler().createLocalReference(context, second, context.getCurrentType()); result += "ognl.OgnlOps.booleanValue(" + first + ")"; result += " ? "; result += first; result += " : "; result += second; context.setCurrentObject(target); context.setCurrentType(Boolean.TYPE); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return result; } } ================================================ FILE: ognl/src/main/java/ognl/ASTProject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; public class ASTProject> extends SimpleNode { @Serial private static final long serialVersionUID = -1429427100657574682L; public ASTProject(int id) { super(id); } public ASTProject(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Node expr = children[0]; List answer = new ArrayList<>(); ElementsAccessor elementsAccessor = OgnlRuntime.getElementsAccessor(OgnlRuntime.getTargetClass(source)); for (Enumeration e = elementsAccessor.getElements(source); e.hasMoreElements(); ) { answer.add(expr.getValue(context, e.nextElement())); } return answer; } public String toString() { return "{ " + children[0] + " }"; } public String toGetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Projection expressions not supported as native java yet."); } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Projection expressions not supported as native java yet."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTProperty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.UnsupportedCompilationException; import java.beans.IndexedPropertyDescriptor; import java.beans.PropertyDescriptor; import java.io.Serial; import java.lang.reflect.Method; import java.util.Iterator; public class ASTProperty> extends SimpleNode implements NodeType { @Serial private static final long serialVersionUID = -3670610950640379714L; private boolean indexedAccess = false; private Class getterClass; private Class setterClass; public ASTProperty(int id) { super(id); } public void setIndexedAccess(boolean value) { indexedAccess = value; } /** * Returns true if this property is itself an index reference. * * @return true if this property is an index reference, false otherwise. */ public boolean isIndexedAccess() { return indexedAccess; } /** * Returns true if this property is described by an IndexedPropertyDescriptor and that if * followed by an index specifier it will call the index get/set methods rather than go through * property accessors. * * @param context the OgnlContext within which to perform the operation. * @param source the Object (indexed property) from which to retrieve the indexed property type. * @return the int representing the indexed property type of source. * @throws OgnlException if source is not an indexed property. */ public int getIndexedPropertyType(C context, Object source) throws OgnlException { Class type = context.getCurrentType(); Class prevType = context.getPreviousType(); try { if (!isIndexedAccess()) { Object property = getProperty(context, source); if (property instanceof String) { return OgnlRuntime.getIndexedPropertyType((source == null) ? null : OgnlRuntime.getCompiler().getInterfaceClass(source.getClass()), (String) property); } } return OgnlRuntime.INDEXED_PROPERTY_NONE; } finally { context.setCurrentObject(source); context.setCurrentType(type); context.setPreviousType(prevType); } } public Object getProperty(C context, Object source) throws OgnlException { return children[0].getValue(context, context.getRoot()); } protected Object getValueBody(C context, Object source) throws OgnlException { Object property = getProperty(context, source); Object result = OgnlRuntime.getProperty(context, source, property); if (result == null) { NullHandler nullHandler = OgnlRuntime.getNullHandler(OgnlRuntime.getTargetClass(source)); result = nullHandler.nullPropertyValue(context, source, property); } return result; } protected void setValueBody(C context, Object target, Object value) throws OgnlException { OgnlRuntime.setProperty(context, target, getProperty(context, target), value); } public boolean isNodeSimpleProperty(C context) throws OgnlException { return (children != null) && (children.length == 1) && ((SimpleNode) children[0]).isConstant(context); } public Class getGetterClass() { return getterClass; } public Class getSetterClass() { return setterClass; } public String toString() { String result; if (isIndexedAccess()) { result = "[" + children[0] + "]"; } else { result = ((ASTConst) children[0]).getValue().toString(); } return result; } public String toGetSourceString(C context, Object target) { if (context.getCurrentObject() == null) throw new UnsupportedCompilationException("Current target is null."); String result = ""; Method m = null; try { if (isIndexedAccess()) { Object value = children[0].getValue(context, context.getRoot()); if (value == null || DynamicSubscript.class.isAssignableFrom(value.getClass())) throw new UnsupportedCompilationException("Value passed as indexed property was null or not supported."); // Get root cast string if the child is a type that needs it (like a nested ASTProperty) String srcString = children[0].toGetSourceString(context, context.getRoot()); srcString = ExpressionCompiler.getRootExpression(children[0], context.getRoot(), context) + srcString; if (children[0] instanceof ASTChain) { String cast = (String) context.remove(ExpressionCompiler.PRE_CAST); if (cast != null) srcString = cast + srcString; } if (children[0] instanceof ASTConst && context.getCurrentObject() instanceof String) srcString = "\"" + srcString + "\""; // System.out.println("indexed getting with child srcString: " + srcString + " value class: " + value.getClass() + " and child: " + _children[0].getClass()); if (context.get("_indexedMethod") != null) { m = (Method) context.remove("_indexedMethod"); getterClass = m.getReturnType(); Object indexedValue = OgnlRuntime.callMethod(context, target, m.getName(), new Object[]{value}); context.setCurrentType(getterClass); context.setCurrentObject(indexedValue); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); return "." + m.getName() + "(" + srcString + ")"; } else { PropertyAccessor p = OgnlRuntime.getPropertyAccessor(target.getClass()); Object currObj = context.getCurrentObject(); Class currType = context.getCurrentType(); Class prevType = context.getPreviousType(); Object indexVal = p.getProperty(context, target, value); // reset current object for accessor context.setCurrentObject(currObj); context.setCurrentType(currType); context.setPreviousType(prevType); if (children[0] instanceof ASTConst && context.getCurrentObject() instanceof Number) context.setCurrentType(OgnlRuntime.getPrimitiveWrapperClass(context.getCurrentObject().getClass())); result = p.getSourceAccessor(context, target, srcString); getterClass = context.getCurrentType(); context.setCurrentObject(indexVal); return result; } } String name = ((ASTConst) children[0]).getValue().toString(); if (!Iterator.class.isAssignableFrom(context.getCurrentObject().getClass()) || (Iterator.class.isAssignableFrom(context.getCurrentObject().getClass()) && !name.contains("next"))) { Object currObj = target; try { target = getValue(context, context.getCurrentObject()); } catch (NoSuchPropertyException e) { try { target = getValue(context, context.getRoot()); } catch (NoSuchPropertyException ex) { // ignore } } finally { context.setCurrentObject(currObj); } } PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(context.getCurrentObject().getClass(), name); if (pd != null && pd.getReadMethod() != null && !context.getMemberAccess().isAccessible(context, context.getCurrentObject(), pd.getReadMethod(), name)) { throw new UnsupportedCompilationException("Member access forbidden for property " + name + " on class " + context.getCurrentObject().getClass()); } if (this.getIndexedPropertyType(context, context.getCurrentObject()) > 0 && pd != null) { // if an indexed method accessor need to use special property descriptors to find methods if (pd instanceof IndexedPropertyDescriptor) { m = ((IndexedPropertyDescriptor) pd).getIndexedReadMethod(); } else { if (pd instanceof ObjectIndexedPropertyDescriptor) m = ((ObjectIndexedPropertyDescriptor) pd).getIndexedReadMethod(); else throw new OgnlException("property '" + name + "' is not an indexed property"); } if (parent == null) { // the above pd will be the wrong result sometimes, such as methods like getValue(int) vs String[] getValue() m = OgnlRuntime.getReadMethod(context.getCurrentObject().getClass(), name); result = m.getName() + "()"; getterClass = m.getReturnType(); } else { context.put("_indexedMethod", m); } } else { /* System.out.println("astproperty trying to get " + name + " on object target: " + context.getCurrentObject().getClass().getName() + " current type " + context.getCurrentType() + " current accessor " + context.getCurrentAccessor() + " prev type " + context.getPreviousType() + " prev accessor " + context.getPreviousAccessor());*/ PropertyAccessor pa = OgnlRuntime.getPropertyAccessor(context.getCurrentObject().getClass()); if (context.getCurrentObject().getClass().isArray()) { if (pd == null) { pd = OgnlRuntime.getProperty(context.getCurrentObject().getClass(), name); if (pd != null && pd.getReadMethod() != null) { m = pd.getReadMethod(); result = pd.getName(); } else { getterClass = int.class; context.setCurrentAccessor(context.getCurrentObject().getClass()); context.setCurrentType(int.class); result = "." + name; } } } else { if (pd != null && pd.getReadMethod() != null) { m = pd.getReadMethod(); result = "." + m.getName() + "()"; } else if (pa != null) { Object currObj = context.getCurrentObject(); Class currType = context.getCurrentType(); Class prevType = context.getPreviousType(); String srcString = children[0].toGetSourceString(context, context.getRoot()); if (children[0] instanceof ASTConst && context.getCurrentObject() instanceof String) { srcString = "\"" + srcString + "\""; } context.setCurrentObject(currObj); context.setCurrentType(currType); context.setPreviousType(prevType); result = pa.getSourceAccessor(context, context.getCurrentObject(), srcString); getterClass = context.getCurrentType(); } } } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } // set known property types for NodeType interface when possible if (m != null) { getterClass = m.getReturnType(); context.setCurrentType(m.getReturnType()); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); } context.setCurrentObject(target); return result; } Method getIndexedWriteMethod(PropertyDescriptor pd) { if (pd instanceof IndexedPropertyDescriptor) { return ((IndexedPropertyDescriptor) pd).getIndexedWriteMethod(); } else if (pd instanceof ObjectIndexedPropertyDescriptor) { return ((ObjectIndexedPropertyDescriptor) pd).getIndexedWriteMethod(); } return null; } public String toSetSourceString(C context, Object target) { String result = ""; Method m = null; if (context.getCurrentObject() == null) throw new UnsupportedCompilationException("Current target is null."); try { if (isIndexedAccess()) { Object value = children[0].getValue(context, context.getRoot()); if (value == null) throw new UnsupportedCompilationException("Value passed as indexed property is null, can't enhance statement to bytecode."); String srcString = children[0].toGetSourceString(context, context.getRoot()); srcString = ExpressionCompiler.getRootExpression(children[0], context.getRoot(), context) + srcString; if (children[0] instanceof ASTChain) { String cast = (String) context.remove(ExpressionCompiler.PRE_CAST); if (cast != null) srcString = cast + srcString; } if (children[0] instanceof ASTConst && context.getCurrentObject() instanceof String) { srcString = "\"" + srcString + "\""; } if (context.get("_indexedMethod") != null) { m = (Method) context.remove("_indexedMethod"); PropertyDescriptor pd = (PropertyDescriptor) context.remove("_indexedDescriptor"); boolean lastChild = lastChild(context); if (lastChild) { m = getIndexedWriteMethod(pd); if (m == null) throw new UnsupportedCompilationException("Indexed property has no corresponding write method."); } setterClass = m.getParameterTypes()[0]; Object indexedValue = null; if (!lastChild) indexedValue = OgnlRuntime.callMethod(context, target, m.getName(), new Object[]{value}); context.setCurrentType(setterClass); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); if (!lastChild) { context.setCurrentObject(indexedValue); return "." + m.getName() + "(" + srcString + ")"; } else { return "." + m.getName() + "(" + srcString + ", $3)"; } } else { PropertyAccessor p = OgnlRuntime.getPropertyAccessor(target.getClass()); Object currObj = context.getCurrentObject(); Class currType = context.getCurrentType(); Class prevType = context.getPreviousType(); Object indexVal = p.getProperty(context, target, value); // reset current object for accessor context.setCurrentObject(currObj); context.setCurrentType(currType); context.setPreviousType(prevType); if (children[0] instanceof ASTConst && context.getCurrentObject() instanceof Number) context.setCurrentType(OgnlRuntime.getPrimitiveWrapperClass(context.getCurrentObject().getClass())); result = lastChild(context) ? p.getSourceSetter(context, target, srcString) : p.getSourceAccessor(context, target, srcString); getterClass = context.getCurrentType(); context.setCurrentObject(indexVal); return result; } } String name = ((ASTConst) children[0]).getValue().toString(); if (!Iterator.class.isAssignableFrom(context.getCurrentObject().getClass()) || (Iterator.class.isAssignableFrom(context.getCurrentObject().getClass()) && !name.contains("next"))) { Object currObj = target; try { target = getValue(context, context.getCurrentObject()); } catch (NoSuchPropertyException e) { try { target = getValue(context, context.getRoot()); } catch (NoSuchPropertyException ignored) { } } finally { context.setCurrentObject(currObj); } } PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(OgnlRuntime.getCompiler().getInterfaceClass(context.getCurrentObject().getClass()), name); if (pd != null) { Method pdMethod = lastChild(context) ? pd.getWriteMethod() : pd.getReadMethod(); if (pdMethod != null && !context.getMemberAccess().isAccessible(context, context.getCurrentObject(), pdMethod, name)) { throw new UnsupportedCompilationException("Member access forbidden for property " + name + " on class " + context.getCurrentObject().getClass()); } } if (pd != null && this.getIndexedPropertyType(context, context.getCurrentObject()) > 0) { // if an indexed method accessor need to use special property descriptors to find methods if (pd instanceof IndexedPropertyDescriptor ipd) { m = lastChild(context) ? ipd.getIndexedWriteMethod() : ipd.getIndexedReadMethod(); } else { if (pd instanceof ObjectIndexedPropertyDescriptor opd) { m = lastChild(context) ? opd.getIndexedWriteMethod() : opd.getIndexedReadMethod(); } else { throw new OgnlException("property '" + name + "' is not an indexed property"); } } if (parent == null) { // the above pd will be the wrong result sometimes, such as methods like getValue(int) vs String[] getValue() m = OgnlRuntime.getWriteMethod(context.getCurrentObject().getClass(), name); Class parm = m.getParameterTypes()[0]; String cast = parm.isArray() ? ExpressionCompiler.getCastString(parm) : parm.getName(); result = m.getName() + "((" + cast + ")$3)"; setterClass = parm; } else { context.put("_indexedMethod", m); context.put("_indexedDescriptor", pd); } } else { PropertyAccessor pa = OgnlRuntime.getPropertyAccessor(context.getCurrentObject().getClass()); if (target != null) setterClass = target.getClass(); if (parent != null && pd != null && pa == null) { m = pd.getReadMethod(); result = m.getName() + "()"; } else { if (context.getCurrentObject().getClass().isArray()) { result = ""; } else if (pa != null) { Object currObj = context.getCurrentObject(); //Class currType = context.getCurrentType(); //Class prevType = context.getPreviousType(); String srcString = children[0].toGetSourceString(context, context.getRoot()); if (children[0] instanceof ASTConst && context.getCurrentObject() instanceof String) { srcString = "\"" + srcString + "\""; } context.setCurrentObject(currObj); //context.setCurrentType(currType); //context.setPreviousType(prevType); if (!lastChild(context)) { result = pa.getSourceAccessor(context, context.getCurrentObject(), srcString); } else { result = pa.getSourceSetter(context, context.getCurrentObject(), srcString); } getterClass = context.getCurrentType(); } } } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } context.setCurrentObject(target); if (m != null) { context.setCurrentType(m.getReturnType()); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); } return result; } } ================================================ FILE: ognl/src/main/java/ognl/ASTRemainder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTRemainder> extends NumericExpression { @Serial private static final long serialVersionUID = -3811328375555237590L; public ASTRemainder(int id) { super(id); } public ASTRemainder(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.remainder(v1, v2); } public String getExpressionOperator(int index) { return "%"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTRootVarRef.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import java.io.Serial; public class ASTRootVarRef> extends ASTVarRef { @Serial private static final long serialVersionUID = 6329715748764710773L; public ASTRootVarRef(int id) { super(id); } public ASTRootVarRef(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { return context.getRoot(); } protected void setValueBody(C context, Object target, Object value) throws OgnlException { context.setRoot(value); } public String toString() { return "#root"; } public String toGetSourceString(C context, Object target) { if (target != null) { getterClass = target.getClass(); } if (getterClass != null) { context.setCurrentType(getterClass); } if (parent == null || (getterClass != null && getterClass.isArray())) return ""; else return ExpressionCompiler.getRootExpression(this, target, context); } public String toSetSourceString(C context, Object target) { if (parent == null || (getterClass != null && getterClass.isArray())) return ""; else return "$3"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTSelect.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; public class ASTSelect> extends SimpleNode { @Serial private static final long serialVersionUID = 5968173710894672384L; public ASTSelect(int id) { super(id); } public ASTSelect(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Node expr = children[0]; List answer = new ArrayList<>(); ElementsAccessor elementsAccessor = OgnlRuntime.getElementsAccessor(OgnlRuntime.getTargetClass(source)); for (Enumeration e = elementsAccessor.getElements(source); e.hasMoreElements(); ) { Object next = e.nextElement(); if (OgnlOps.booleanValue(expr.getValue(context, next))) { answer.add(next); } } return answer; } public String toString() { return "{? " + children[0] + " }"; } public String toGetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Eval expressions not supported as native java yet."); } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Eval expressions not supported as native java yet."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTSelectFirst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; public class ASTSelectFirst> extends SimpleNode { @Serial private static final long serialVersionUID = -8589584203833344367L; public ASTSelectFirst(int id) { super(id); } public ASTSelectFirst(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Node expr = children[0]; List answer = new ArrayList<>(); ElementsAccessor elementsAccessor = OgnlRuntime.getElementsAccessor(OgnlRuntime.getTargetClass(source)); for (Enumeration e = elementsAccessor.getElements(source); e.hasMoreElements(); ) { Object next = e.nextElement(); if (OgnlOps.booleanValue(expr.getValue(context, next))) { answer.add(next); break; } } return answer; } public String toString() { return "{^ " + children[0] + " }"; } public String toGetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Eval expressions not supported as native java yet."); } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Eval expressions not supported as native java yet."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTSelectLast.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; public class ASTSelectLast> extends SimpleNode { @Serial private static final long serialVersionUID = 4669626401984872936L; public ASTSelectLast(int id) { super(id); } public ASTSelectLast(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Node expr = children[0]; List answer = new ArrayList<>(); ElementsAccessor elementsAccessor = OgnlRuntime.getElementsAccessor(OgnlRuntime.getTargetClass(source)); for (Enumeration e = elementsAccessor.getElements(source); e.hasMoreElements(); ) { Object next = e.nextElement(); if (OgnlOps.booleanValue(expr.getValue(context, next))) { answer.clear(); answer.add(next); } } return answer; } public String toString() { return "{$ " + children[0] + " }"; } public String toGetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Eval expressions not supported as native java yet."); } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Eval expressions not supported as native java yet."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTSequence.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.OrderedReturn; import java.io.Serial; public class ASTSequence> extends SimpleNode implements NodeType, OrderedReturn { @Serial private static final long serialVersionUID = -6645566648865448882L; private Class getterClass; private String lastExpression; private String coreExpression; public ASTSequence(int id) { super(id); } public ASTSequence(OgnlParser p, int id) { super(p, id); } public void jjtClose() { flattenTree(); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = null; for (Node child : children) { result = child.getValue(context, source); } return result; // The result is just the last one we saw. } protected void setValueBody(C context, Object target, Object value) throws OgnlException { int last = children.length - 1; for (int i = 0; i < last; ++i) { children[i].getValue(context, target); } children[last].setValue(context, target, value); } public Class getGetterClass() { return getterClass; } public Class getSetterClass() { return null; } public String getLastExpression() { return lastExpression; } public String getCoreExpression() { return coreExpression; } public String toString() { StringBuilder result = new StringBuilder(); for (int i = 0; i < children.length; ++i) { if (i > 0) { result.append(", "); } result.append(children[i]); } return result.toString(); } public String toSetSourceString(C context, Object target) { return ""; } public String toGetSourceString(C context, Object target) { String result = ""; NodeType _lastType = null; for (int i = 0; i < children.length; ++i) { String seqValue = children[i].toGetSourceString(context, target); if ((i + 1) < children.length && children[i] instanceof ASTOr) { seqValue = "(" + seqValue + ")"; } if (i > 0 && children[i] instanceof ASTProperty && seqValue != null && seqValue.trim().length() > 0) { String pre = (String) context.get("_currentChain"); if (pre == null) { pre = ""; } seqValue = ExpressionCompiler.getRootExpression(children[i], context.getRoot(), context) + pre + seqValue; context.setCurrentAccessor(context.getRoot().getClass()); } if ((i + 1) >= children.length) { coreExpression = result; lastExpression = seqValue; } if (seqValue != null && seqValue.trim().length() > 0 && (i + 1) < children.length) { result += seqValue + ";"; } else if (seqValue != null && seqValue.trim().length() > 0) { result += seqValue; } // set last known type from last child with a type if (children[i] instanceof NodeType && ((NodeType) children[i]).getGetterClass() != null) { _lastType = (NodeType) children[i]; } } if (_lastType != null) { getterClass = _lastType.getGetterClass(); } return result; } public boolean isSequence(C context) { return true; } } ================================================ FILE: ognl/src/main/java/ognl/ASTShiftLeft.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTShiftLeft> extends NumericExpression { @Serial private static final long serialVersionUID = 736476424938506175L; public ASTShiftLeft(int id) { super(id); } public ASTShiftLeft(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.shiftLeft(v1, v2); } public String getExpressionOperator(int index) { return "<<"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTShiftRight.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTShiftRight> extends NumericExpression { @Serial private static final long serialVersionUID = 9190099431531689455L; public ASTShiftRight(int id) { super(id); } public ASTShiftRight(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.shiftRight(v1, v2); } public String getExpressionOperator(int index) { return ">>"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTStaticField.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class ASTStaticField> extends SimpleNode implements NodeType { @Serial private static final long serialVersionUID = -6755053323607452367L; private String className; private String fieldName; private Class getterClass; public ASTStaticField(int id) { super(id); } public ASTStaticField(OgnlParser p, int id) { super(p, id); } /** * Called from parser action. */ void init(String className, String fieldName) { this.className = className; this.fieldName = fieldName; } protected Object getValueBody(C context, Object source) throws OgnlException { return OgnlRuntime.getStaticField(context, className, fieldName); } public boolean isNodeConstant(C context) throws OgnlException { boolean result = false; Exception reason = null; try { Class c = OgnlRuntime.classForName(context, className); /* * Check for virtual static field "class"; this cannot interfere with normal static * fields because it is a reserved word. It is considered constant. */ if (fieldName.equals("class")) { result = true; } else if (c.isEnum()) { result = true; } else { Field f = OgnlRuntime.getField(c, fieldName); if (f == null) { throw new NoSuchFieldException(fieldName); } if (!Modifier.isStatic(f.getModifiers())) throw new OgnlException("Field " + fieldName + " of class " + className + " is not static"); result = Modifier.isFinal(f.getModifiers()); } } catch (ClassNotFoundException | NoSuchFieldException | SecurityException e) { reason = e; } if (reason != null) throw new OgnlException("Could not get static field " + fieldName + " from class " + className, reason); return result; } private Class getFieldClass(C context) throws OgnlException { Exception reason; try { Class c = OgnlRuntime.classForName(context, className); /* * Check for virtual static field "class"; this cannot interfere with normal static * fields because it is a reserved word. It is considered constant. */ if (fieldName.equals("class")) { return c; } else if (c.isEnum()) { return c; } else { Field f = c.getField(fieldName); return f.getType(); } } catch (ClassNotFoundException | NoSuchFieldException | SecurityException e) { reason = e; } throw new OgnlException("Could not get static field " + fieldName + " from class " + className, reason); } public Class getGetterClass() { return getterClass; } public Class getSetterClass() { return getterClass; } public String toString() { return "@" + className + "@" + fieldName; } public String toGetSourceString(C context, Object target) { try { Object obj = OgnlRuntime.getStaticField(context, className, fieldName); context.setCurrentObject(obj); getterClass = getFieldClass(context); context.setCurrentType(getterClass); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return className + "." + fieldName; } public String toSetSourceString(C context, Object target) { try { Object obj = OgnlRuntime.getStaticField(context, className, fieldName); context.setCurrentObject(obj); getterClass = getFieldClass(context); context.setCurrentType(getterClass); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return className + "." + fieldName; } } ================================================ FILE: ognl/src/main/java/ognl/ASTStaticMethod.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; import java.lang.reflect.Method; import java.util.Objects; public class ASTStaticMethod> extends SimpleNode implements NodeType { @Serial private static final long serialVersionUID = -242038874659625466L; private String className; private String methodName; private Class getterClass; public ASTStaticMethod(int id) { super(id); } public ASTStaticMethod(OgnlParser p, int id) { super(p, id); } /** * Called from parser action. */ void init(String className, String methodName) { this.className = className; this.methodName = methodName; } protected Object getValueBody(C context, Object source) throws OgnlException { Object[] args = new Object[jjtGetNumChildren()]; Object root = context.getRoot(); for (int i = 0, icount = args.length; i < icount; ++i) { args[i] = children[i].getValue(context, root); } return OgnlRuntime.callStaticMethod(context, className, methodName, args); } public Class getGetterClass() { return getterClass; } public Class getSetterClass() { return getterClass; } public String toString() { StringBuilder result = new StringBuilder("@" + className + "@" + methodName); result.append("("); if ((children != null) && (children.length > 0)) { for (int i = 0; i < children.length; i++) { if (i > 0) { result.append(", "); } result.append(children[i]); } } result.append(")"); return result.toString(); } public String toGetSourceString(C context, Object target) { StringBuilder result = new StringBuilder(className + "#" + methodName + "("); try { Class clazz = OgnlRuntime.classForName(context, className); Method m = OgnlRuntime.getMethod(context, clazz, methodName, children, true); if (m == null) { throw new UnsupportedCompilationException("Unable to find class/method combo " + className + " / " + methodName); } if (!context.getMemberAccess().isAccessible(context, clazz, m, methodName)) { throw new UnsupportedCompilationException("Method is not accessible, check your jvm runtime security settings. " + "For static class method " + className + " / " + methodName); } if ((children != null) && (children.length > 0)) { Class[] parms = m.getParameterTypes(); for (int i = 0; i < children.length; i++) { if (i > 0) { result.append(", "); } Class prevType = context.getCurrentType(); Object value = children[i].getValue(context, context.getRoot()); String parmString = children[i].toGetSourceString(context, context.getRoot()); if (parmString == null || parmString.trim().isEmpty()) parmString = "null"; // to undo type setting of constants when used as method parameters if (children[i] instanceof ASTConst) { context.setCurrentType(prevType); } parmString = ExpressionCompiler.getRootExpression(children[i], context.getRoot(), context) + parmString; String cast = ""; if (ExpressionCompiler.shouldCast(children[i])) { cast = (String) context.remove(ExpressionCompiler.PRE_CAST); } if (cast == null) cast = ""; if (!(children[i] instanceof ASTConst)) parmString = cast + parmString; Class valueClass = value != null ? value.getClass() : null; if (NodeType.class.isAssignableFrom(children[i].getClass())) valueClass = ((NodeType) children[i]).getGetterClass(); if (valueClass != parms[i]) { if (parms[i].isArray()) { parmString = OgnlRuntime.getCompiler() .createLocalReference(context, "(" + ExpressionCompiler.getCastString(parms[i]) + ")ognl.OgnlOps.toArray(" + parmString + ", " + parms[i].getComponentType().getName() + ".class, true)", parms[i] ); } else if (parms[i].isPrimitive()) { Class wrapClass = OgnlRuntime.getPrimitiveWrapperClass(parms[i]); parmString = OgnlRuntime.getCompiler().createLocalReference(context, "((" + wrapClass.getName() + ")ognl.OgnlOps.convertValue(" + parmString + "," + wrapClass.getName() + ".class, true))." + OgnlRuntime.getNumericValueGetter(wrapClass), parms[i] ); } else if (parms[i] != Object.class) { parmString = OgnlRuntime.getCompiler() .createLocalReference(context, "(" + parms[i].getName() + ")ognl.OgnlOps.convertValue(" + parmString + "," + parms[i].getName() + ".class)", parms[i] ); } else if ((children[i] instanceof NodeType && ((NodeType) children[i]).getGetterClass() != null && Number.class.isAssignableFrom(((NodeType) children[i]).getGetterClass())) || Objects.requireNonNull(valueClass).isPrimitive()) { parmString = " ($w) " + parmString; } } result.append(parmString); } } result.append(")"); try { Object contextObj = getValueBody(context, target); context.setCurrentObject(contextObj); } catch (Throwable t) { // ignore } getterClass = m.getReturnType(); context.setCurrentType(m.getReturnType()); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return result.toString(); } public String toSetSourceString(C context, Object target) { return toGetSourceString(context, target); } } ================================================ FILE: ognl/src/main/java/ognl/ASTSubtract.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTSubtract> extends NumericExpression { @Serial private static final long serialVersionUID = -7677636672357878823L; public ASTSubtract(int id) { super(id); } public ASTSubtract(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.subtract(v1, v2); } public String getExpressionOperator(int index) { return "-"; } } ================================================ FILE: ognl/src/main/java/ognl/ASTTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTTest> extends ExpressionNode { @Serial private static final long serialVersionUID = -5027899942735313771L; public ASTTest(int id) { super(id); } public ASTTest(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object test = children[0].getValue(context, source); int branch = OgnlOps.booleanValue(test) ? 1 : 2; return children[branch].getValue(context, source); } protected void setValueBody(C context, Object target, Object value) throws OgnlException { Object test = children[0].getValue(context, target); int branch = OgnlOps.booleanValue(test) ? 1 : 2; children[branch].setValue(context, target, value); } public String getExpressionOperator(int index) { return (index == 1) ? "?" : ":"; } public String toGetSourceString(C context, Object target) { if (target == null) throw new UnsupportedCompilationException("evaluation resulted in null expression."); if (children.length != 3) throw new UnsupportedCompilationException("Can only compile test expressions with two children." + children.length); String result = ""; try { String first = OgnlRuntime.getChildSource(context, target, children[0]); if (!OgnlRuntime.isBoolean(first) && !context.getCurrentType().isPrimitive()) first = OgnlRuntime.getCompiler().createLocalReference(context, first, context.getCurrentType()); if (children[0] instanceof ExpressionNode) { first = "(" + first + ")"; } String second = OgnlRuntime.getChildSource(context, target, children[1]); Class secondType = context.getCurrentType(); if (!OgnlRuntime.isBoolean(second) && !context.getCurrentType().isPrimitive()) second = OgnlRuntime.getCompiler().createLocalReference(context, second, context.getCurrentType()); if (children[1] instanceof ExpressionNode) { second = "(" + second + ")"; } String third = OgnlRuntime.getChildSource(context, target, children[2]); Class thirdType = context.getCurrentType(); if (!OgnlRuntime.isBoolean(third) && !context.getCurrentType().isPrimitive()) third = OgnlRuntime.getCompiler().createLocalReference(context, third, context.getCurrentType()); if (children[2] instanceof ExpressionNode) { third = "(" + third + ")"; } boolean mismatched = (secondType.isPrimitive() && !thirdType.isPrimitive()) || (!secondType.isPrimitive() && thirdType.isPrimitive()); result += "ognl.OgnlOps.booleanValue(" + first + ")"; result += " ? "; result += (mismatched ? " ($w) " : "") + second; result += " : "; result += (mismatched ? " ($w) " : "") + third; context.setCurrentObject(target); context.setCurrentType(mismatched ? Object.class : secondType); return result; } catch (NullPointerException e) { // expected to happen in some instances throw new UnsupportedCompilationException("evaluation resulted in null expression."); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } } ================================================ FILE: ognl/src/main/java/ognl/ASTThisVarRef.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTThisVarRef> extends ASTVarRef { @Serial private static final long serialVersionUID = -398279289206640857L; public ASTThisVarRef(int id) { super(id); } public ASTThisVarRef(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { return context.getCurrentObject(); } protected void setValueBody(C context, Object target, Object value) throws OgnlException { context.setCurrentObject(value); } public String toString() { return "#this"; } public String toGetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Unable to compile this references."); } public String toSetSourceString(C context, Object target) { throw new UnsupportedCompilationException("Unable to compile this references."); } } ================================================ FILE: ognl/src/main/java/ognl/ASTUnsignedShiftRight.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTUnsignedShiftRight> extends NumericExpression { @Serial private static final long serialVersionUID = -5612682130687347598L; public ASTUnsignedShiftRight(int id) { super(id); } public ASTUnsignedShiftRight(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(C context, Object source) throws OgnlException { Object v1 = children[0].getValue(context, source); Object v2 = children[1].getValue(context, source); return OgnlOps.unsignedShiftRight(v1, v2); } public String getExpressionOperator(int index) { return ">>>"; } public String toGetSourceString(C context, Object target) { String result; try { String child1 = OgnlRuntime.getChildSource(context, target, children[0]); child1 = coerceToNumeric(child1, context, children[0]); String child2 = OgnlRuntime.getChildSource(context, target, children[1]); child2 = coerceToNumeric(child2, context, children[1]); Object v1 = children[0].getValue(context, target); int type = OgnlOps.getNumericType(v1); if (type <= OgnlOps.INT) { child1 = "(int)" + child1; child2 = "(int)" + child2; } result = child1 + " >>> " + child2; context.setCurrentType(Integer.TYPE); context.setCurrentObject(getValueBody(context, target)); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return result; } } ================================================ FILE: ognl/src/main/java/ognl/ASTVarRef.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.OrderedReturn; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; public class ASTVarRef> extends SimpleNode implements NodeType, OrderedReturn { @Serial private static final long serialVersionUID = -3144828856498560444L; private String name; protected Class getterClass; protected String core; protected String last; public ASTVarRef(int id) { super(id); } public ASTVarRef(OgnlParser p, int id) { super(p, id); } void setName(String name) { this.name = name; } protected Object getValueBody(C context, Object source) throws OgnlException { return context.get(name); } protected void setValueBody(C context, Object target, Object value) throws OgnlException { context.put(name, value); } public Class getGetterClass() { return getterClass; } public Class getSetterClass() { return null; } public String getCoreExpression() { return core; } public String getLastExpression() { return last; } public String toString() { return "#" + name; } public String toGetSourceString(C context, Object target) { Object value = context.get(name); if (value != null) { getterClass = value.getClass(); } context.setCurrentType(getterClass); context.setCurrentAccessor(context.getClass()); context.setCurrentObject(value); if (context.getCurrentObject() == null) { throw new UnsupportedCompilationException("Current context object is null, can't compile var reference."); } String pre = ""; String post = ""; if (context.getCurrentType() != null) { pre = "((" + OgnlRuntime.getCompiler().getInterfaceClass(context.getCurrentType()).getName() + ")"; post = ")"; } if (parent instanceof ASTAssign) { core = "$1.put(\"" + name + "\","; last = pre + "$1.get(\"" + name + "\")" + post; return core; } return pre + "$1.get(\"" + name + "\")" + post; } public String toSetSourceString(C context, Object target) { return toGetSourceString(context, target); } } ================================================ FILE: ognl/src/main/java/ognl/ASTXor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.io.Serial; public class ASTXor> extends NumericExpression { @Serial private static final long serialVersionUID = 5707892722708142594L; public ASTXor(int id) { super(id); } public ASTXor(OgnlParser p, int id) { super(p, id); } public void jjtClose() { flattenTree(); } protected Object getValueBody(C context, Object source) throws OgnlException { Object result = children[0].getValue(context, source); for (int i = 1; i < children.length; ++i) result = OgnlOps.binaryXor(result, children[i].getValue(context, source)); return result; } public String getExpressionOperator(int index) { return "^"; } } ================================================ FILE: ognl/src/main/java/ognl/AbstractMemberAccess.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Member; /** * Used as a based class */ abstract public class AbstractMemberAccess> implements MemberAccess { public Object setup(C context, Object target, Member member, String propertyName) { return null; } public void restore(C context, Object target, Member member, String propertyName, Object state) { } } ================================================ FILE: ognl/src/main/java/ognl/AccessibleObjectHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.AccessibleObject; /** * Provides a mechanism for changing the accessibility of AccessibleObject instances. * * @since 3.1.24 */ public interface AccessibleObjectHandler { /** * Changes the accessibility of the given AccessibleObject. * * @param accessibleObject the AccessibleObject upon which to apply the flag. * @param flag the new accessible flag value. */ default void setAccessible(AccessibleObject accessibleObject, boolean flag) { accessibleObject.setAccessible(flag); } } ================================================ FILE: ognl/src/main/java/ognl/ArrayElementsAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Array; import java.util.Enumeration; /** * Implementation of ElementsAccessor that returns an iterator over a Java array. */ public class ArrayElementsAccessor implements ElementsAccessor { public Enumeration getElements(final Object target) { return new Enumeration() { private final int count = Array.getLength(target); private int index = 0; public boolean hasMoreElements() { return index < count; } public Object nextElement() { return Array.get(target, index++); } }; } } ================================================ FILE: ognl/src/main/java/ognl/ArrayPropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Array; /** * Implementation of PropertyAccessor that uses numbers and dynamic subscripts as properties to * index into Java arrays. */ public class ArrayPropertyAccessor> extends ObjectPropertyAccessor implements PropertyAccessor { public Object getProperty(C context, Object target, Object name) throws OgnlException { Object result = null; if (name instanceof String) { if (name.equals("length")) { result = Array.getLength(target); } else { result = super.getProperty(context, target, name); } } else { Object index = name; if (index instanceof DynamicSubscript) { int len = Array.getLength(target); switch (((DynamicSubscript) index).getFlag()) { case DynamicSubscript.ALL: result = Array.newInstance(target.getClass().getComponentType(), len); System.arraycopy(target, 0, result, 0, len); break; case DynamicSubscript.FIRST: index = (len > 0) ? 0 : -1; break; case DynamicSubscript.MID: index = (len > 0) ? (len / 2) : -1; break; case DynamicSubscript.LAST: index = (len > 0) ? (len - 1) : -1; break; } } if (result == null) { if (index instanceof Number) { int i = ((Number) index).intValue(); result = (i >= 0) ? Array.get(target, i) : null; } else { throw new NoSuchPropertyException(target, index); } } } return result; } public void setProperty(C context, Object target, Object name, Object value) throws OgnlException { Object index = name; boolean isNumber = (index instanceof Number); if (isNumber || (index instanceof DynamicSubscript)) { TypeConverter converter = context.getTypeConverter(); Object convertedValue = converter.convertValue(context, target, null, name.toString(), value, target.getClass().getComponentType()); if (isNumber) { int i = ((Number) index).intValue(); if (i >= 0) { Array.set(target, i, convertedValue); } } else { int len = Array.getLength(target); switch (((DynamicSubscript) index).getFlag()) { case DynamicSubscript.ALL: System.arraycopy(target, 0, convertedValue, 0, len); return; case DynamicSubscript.FIRST: index = (len > 0) ? 0 : -1; break; case DynamicSubscript.MID: index = (len > 0) ? (len / 2) : -1; break; case DynamicSubscript.LAST: index = (len > 0) ? (len - 1) : -1; break; } } } else { if (name instanceof String) { super.setProperty(context, target, name, value); } else { throw new NoSuchPropertyException(target, index); } } } public String getSourceAccessor(C context, Object target, Object index) { String indexStr = index.toString(); // need to convert to primitive for list index access // System.out.println("index class " + index.getClass() + " current type " + context.getCurrentType() + " current object class " + context.getCurrentObject().getClass()); if (context.getCurrentType() != null && !context.getCurrentType().isPrimitive() && Number.class.isAssignableFrom(context.getCurrentType())) { indexStr += "." + OgnlRuntime.getNumericValueGetter(context.getCurrentType()); } else if (context.getCurrentObject() != null && Number.class.isAssignableFrom(context.getCurrentObject().getClass()) && !context.getCurrentType().isPrimitive()) { // means it needs to be cast first as well String toString = index instanceof String && context.getCurrentType() != Object.class ? "" : ".toString()"; indexStr = "ognl.OgnlOps#getIntValue(" + indexStr + toString + ")"; } context.setCurrentAccessor(target.getClass()); context.setCurrentType(target.getClass().getComponentType()); return "[" + indexStr + "]"; } public String getSourceSetter(C context, Object target, Object index) { String indexStr = index.toString(); // need to convert to primitive for list index access if (context.getCurrentType() != null && !context.getCurrentType().isPrimitive() && Number.class.isAssignableFrom(context.getCurrentType())) { indexStr += "." + OgnlRuntime.getNumericValueGetter(context.getCurrentType()); } else if (context.getCurrentObject() != null && Number.class.isAssignableFrom(context.getCurrentObject().getClass()) && !context.getCurrentType().isPrimitive()) { // means it needs to be cast first as well String toString = index instanceof String && context.getCurrentType() != Object.class ? "" : ".toString()"; indexStr = "ognl.OgnlOps#getIntValue(" + indexStr + toString + ")"; } Class type = target.getClass().isArray() ? target.getClass().getComponentType() : target.getClass(); context.setCurrentAccessor(target.getClass()); context.setCurrentType(target.getClass().getComponentType()); if (type.isPrimitive()) { Class wrapClass = OgnlRuntime.getPrimitiveWrapperClass(type); return "[" + indexStr + "]=((" + wrapClass.getName() + ")ognl.OgnlOps.convertValue($3," + wrapClass.getName() + ".class, true))." + OgnlRuntime.getNumericValueGetter(wrapClass); } else { return "[" + indexStr + "]=ognl.OgnlOps.convertValue($3," + type.getName() + ".class)"; } } } ================================================ FILE: ognl/src/main/java/ognl/BooleanExpression.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; /** * Base class for boolean expressions. */ public abstract class BooleanExpression> extends ExpressionNode implements NodeType { @Serial private static final long serialVersionUID = -2717192375855680355L; protected Class getterClass; public BooleanExpression(int id) { super(id); } public BooleanExpression(OgnlParser p, int id) { super(p, id); } public Class getGetterClass() { return getterClass; } public Class getSetterClass() { return null; } public String toGetSourceString(C context, Object target) { try { Object value = getValueBody(context, target); if (value != null && Boolean.class.isAssignableFrom(value.getClass())) { getterClass = Boolean.TYPE; } else if (value != null) { getterClass = value.getClass(); } else { getterClass = Boolean.TYPE; } String ret = super.toGetSourceString(context, target); if ("(false)".equals(ret)) { return "false"; } else if ("(true)".equals(ret)) { return "true"; } return ret; } catch (NullPointerException e) { // expected to happen in some instances throw new UnsupportedCompilationException("Evaluation resulted in null expression.", e); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } } ================================================ FILE: ognl/src/main/java/ognl/ClassCacheInspector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * Optional interface that may be registered with {@link OgnlRuntime#setClassCacheInspector(ClassCacheInspector)} * as a means to disallow caching of specific class types. */ public interface ClassCacheInspector { /** * Invoked just before storing a class type within a cache instance. * * @param type The class that is to be stored. * @return True if the class can be cached, false otherwise. */ boolean shouldCache(Class type); } ================================================ FILE: ognl/src/main/java/ognl/ClassResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * This interface defines an object that will resolve a class from a string * and a ognl context table. */ public interface ClassResolver> { Class classForName(String className, C context) throws ClassNotFoundException; } ================================================ FILE: ognl/src/main/java/ognl/CollectionElementsAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Collection; import java.util.Enumeration; /** * Implementation of ElementsAccessor that returns a collection's iterator. */ public class CollectionElementsAccessor implements ElementsAccessor { public Enumeration getElements(Object target) { return new IteratorEnumeration(((Collection) target).iterator()); } } ================================================ FILE: ognl/src/main/java/ognl/ComparisonExpression.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.io.Serial; /** * Base class for types that compare values. */ public abstract class ComparisonExpression> extends BooleanExpression { @Serial private static final long serialVersionUID = -4420582351064321780L; public ComparisonExpression(int id) { super(id); } public ComparisonExpression(OgnlParser p, int id) { super(p, id); } public abstract String getComparisonFunction(); public String toGetSourceString(C context, Object target) { if (target == null) throw new UnsupportedCompilationException("Current target is null, can't compile."); try { Object value = getValueBody(context, target); if (value != null && Boolean.class.isAssignableFrom(value.getClass())) getterClass = Boolean.TYPE; else if (value != null) getterClass = value.getClass(); else getterClass = Boolean.TYPE; // iterate over children to make numeric type detection work properly OgnlRuntime.getChildSource(context, target, children[0]); OgnlRuntime.getChildSource(context, target, children[1]); boolean conversion = OgnlRuntime.shouldConvertNumericTypes(context); String result = conversion ? "(" + getComparisonFunction() + "( ($w) (" : "("; result += OgnlRuntime.getChildSource(context, target, children[0]) + " " + (conversion ? "), ($w) " : getExpressionOperator(0)) + " " + OgnlRuntime.getChildSource(context, target, children[1]); result += conversion ? ")" : ""; context.setCurrentType(Boolean.TYPE); result += ")"; return result; } catch (NullPointerException e) { // expected to happen in some instances throw new UnsupportedCompilationException("evaluation resulted in null expression."); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } } ================================================ FILE: ognl/src/main/java/ognl/DefaultClassResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.concurrent.ConcurrentHashMap; /** * Default class resolution. Uses Class.forName() to look up classes by name. * It also looks in the "java.lang" package if the class named does not give * a package specifier, allowing easier usage of these classes. */ public class DefaultClassResolver> implements ClassResolver { private final ConcurrentHashMap> classes = new ConcurrentHashMap<>(101); public DefaultClassResolver() { super(); } public Class classForName(String className, C context) throws ClassNotFoundException { Class result = classes.get(className); if (result != null) { return (Class) result; } try { result = toClassForName(className); } catch (ClassNotFoundException e) { if (className.indexOf('.') > -1) { throw e; } // The class was not in the default package. // Try prepending 'java.lang.'. try { result = toClassForName("java.lang." + className); } catch (ClassNotFoundException e2) { // Report the specified class name as-is. throw e; } } classes.putIfAbsent(className, result); return (Class) result; } protected Class toClassForName(String className) throws ClassNotFoundException { return Class.forName(className); } } ================================================ FILE: ognl/src/main/java/ognl/DefaultTypeConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Member; /** * Default type conversion. Converts among numeric types and also strings. */ public class DefaultTypeConverter> implements TypeConverter { public DefaultTypeConverter() { super(); } public Object convertValue(C context, Object value, Class toType) { return OgnlOps.convertValue(value, toType); } public Object convertValue(C context, Object target, Member member, String propertyName, Object value, Class toType) { return convertValue(context, value, toType); } } ================================================ FILE: ognl/src/main/java/ognl/DynamicSubscript.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * This class has predefined instances that stand for OGNL's special "dynamic subscripts" * for getting at the first, middle, or last elements of a list. In OGNL expressions, * these subscripts look like special kinds of array indexes: [^] means the first element, * [$] means the last, [|] means the middle, and [*] means the whole list. */ public class DynamicSubscript { public static final int FIRST = 0; public static final int MID = 1; public static final int LAST = 2; public static final int ALL = 3; public static final DynamicSubscript first = new DynamicSubscript(FIRST); public static final DynamicSubscript mid = new DynamicSubscript(MID); public static final DynamicSubscript last = new DynamicSubscript(LAST); public static final DynamicSubscript all = new DynamicSubscript(ALL); private final int flag; private DynamicSubscript(int flag) { this.flag = flag; } public int getFlag() { return flag; } public String toString() { switch (flag) { case FIRST: return "^"; case MID: return "|"; case LAST: return "$"; case ALL: return "*"; default: return "?"; // Won't happen } } } ================================================ FILE: ognl/src/main/java/ognl/ElementsAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; /** * This interface defines a method for getting the "elements" of an object, which means * any objects that naturally would be considered to be contained by the object. So for a * collection, you would expect this method to return all the objects in that collection; * while for an ordinary object you would expect this method to return just that object. * *

An implementation of this interface will often require that its target objects all * be of some particular type. For example, the MapElementsAccessor class requires that * its targets all implement the Map interface. */ public interface ElementsAccessor { /** * Returns an iterator over the elements of the given target object. * * @param target the object to get the elements of * @return an iterator over the elements of the given object * @throws OgnlException if there is an error getting the given object's elements */ Enumeration getElements(Object target) throws OgnlException; } ================================================ FILE: ognl/src/main/java/ognl/EnumerationElementsAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; /** * Implementation of the ElementsAccessor interface for Enumerations, which returns an * iterator that passes its calls through to the target Enumeration. */ public class EnumerationElementsAccessor implements ElementsAccessor { public Enumeration getElements(Object target) { return (Enumeration) target; } } ================================================ FILE: ognl/src/main/java/ognl/EnumerationIterator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; import java.util.Iterator; /** * Object that implements Iterator from an Enumeration */ public class EnumerationIterator implements Iterator { private Enumeration enumeration; public EnumerationIterator(Enumeration enumeration) { this.enumeration = enumeration; } public boolean hasNext() { return enumeration.hasMoreElements(); } public T next() { return enumeration.nextElement(); } public void remove() { throw new UnsupportedOperationException("remove() not supported by Enumeration"); } } ================================================ FILE: ognl/src/main/java/ognl/EnumerationPropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; /** * Implementation of PropertyAccessor that provides "property" reference to * "nextElement" (aliases to "next" also) and "hasMoreElements" (also aliased * to "hasNext"). */ public class EnumerationPropertyAccessor> extends ObjectPropertyAccessor implements PropertyAccessor { public Object getProperty(C context, Object target, Object name) throws OgnlException { Object result; Enumeration e = (Enumeration) target; if (name instanceof String) { if (name.equals("next") || name.equals("nextElement")) { result = e.nextElement(); } else { if (name.equals("hasNext") || name.equals("hasMoreElements")) { result = e.hasMoreElements() ? Boolean.TRUE : Boolean.FALSE; } else { result = super.getProperty(context, target, name); } } } else { result = super.getProperty(context, target, name); } return result; } public void setProperty(C context, Object target, Object name, Object value) throws OgnlException { throw new IllegalArgumentException("can't set property " + name + " on Enumeration"); } } ================================================ FILE: ognl/src/main/java/ognl/Evaluation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * An Evaluation is and object that holds a node being evaluated * and the source from which that node will take extract its * value. It refers to child evaluations that occur as * a result of the nodes' evaluation. */ public class Evaluation> { private SimpleNode node; private Object source; private boolean setOperation; private Object result; private Throwable exception; private Evaluation parent; private Evaluation next; private Evaluation previous; private Evaluation firstChild; private Evaluation lastChild; /** * Constructs a new "get" Evaluation from the node and source given. * * @param node a SimpleNode for this Evaluation. * @param source a source Object for this Evaluation. */ public Evaluation(SimpleNode node, Object source) { super(); this.node = node; this.source = source; } /** * Constructs a new Evaluation from the node and source given. * If setOperation is true this Evaluation represents * a "set" as opposed to a "get". * * @param node a SimpleNode for this Evaluation. * @param source a source Object for this Evaluation. * @param setOperation true to identify this Evaluation as a set operation, false to identify it as a get operation. */ public Evaluation(SimpleNode node, Object source, boolean setOperation) { this(node, source); this.setOperation = setOperation; } /** * Returns the SimpleNode for this Evaluation * * @return the SimpleNode for this Evaluation. */ public SimpleNode getNode() { return node; } /** * Sets the node of the evaluation. Normally applications do not need to * set this. Notable exceptions to this rule are custom evaluators that * choose between navigable objects (as in a multi-root evaluator where * the navigable node is chosen at runtime). * * @param value the SimpleNode to set for this Evaluation. */ public void setNode(SimpleNode value) { node = value; } /** * Returns the source object on which this Evaluation operated. * * @return the source Object operated upon by this Evaluation. */ public Object getSource() { return source; } /** * Sets the source of the evaluation. Normally applications do not need to * set this. Notable exceptions to this rule are custom evaluators that * choose between navigable objects (as in a multi-root evaluator where * the navigable node is chosen at runtime). * * @param value the source Object to be set for this Evaluation. */ public void setSource(Object value) { source = value; } /** * Returns true if this Evaluation represents a set operation. * * @return true if this Evaluation represents a set operation, false otherwise. */ public boolean isSetOperation() { return setOperation; } /** * Marks the Evaluation as a set operation if the value is true, else * marks it as a get operation. * * @param value true to identify this Evaluation as a set operation, false to identify it as a get operation. */ public void setSetOperation(boolean value) { setOperation = value; } /** * Returns the result of the Evaluation, or null if it was a set operation. * * @return the result of the Evaluation (for a get operation), or null (for a set operation). */ public Object getResult() { return result; } /** * Sets the result of the Evaluation. This method is normally only used * interally and should not be set without knowledge of what you are doing. * * @param value the result Object for this Evaluation. */ public void setResult(Object value) { result = value; } /** * Returns the exception that occurred as a result of evaluating the * Evaluation, or null if no exception occurred. * * @return an exception if one occurred during evaluation, or null (no exception) otherwise. */ public Throwable getException() { return exception; } /** * Sets the exception that occurred as a result of evaluating the * Evaluation. This method is normally only used interally and * should not be set without knowledge of what you are doing. * * @param value the Throwable exception that occurred during the evaluation of this Evaluation. */ public void setException(Throwable value) { exception = value; } /** * Returns the parent evaluation of this evaluation. If this returns * null then it is is the root evaluation of a tree. * * @return the parent Evaluation of the current Evaluation, or null if no parent exists. */ public Evaluation getParent() { return parent; } /** * Returns the next sibling of this evaluation. Returns null if * this is the last in a chain of evaluations. * * @return the next sibling Evaluation of the current Evaluation, or null if this is the last Evaluation in a chain. */ public Evaluation getNext() { return next; } /** * Returns the previous sibling of this evaluation. Returns null if * this is the first in a chain of evaluations. * * @return the previous sibling Evaluation of the current Evaluation, or null if this is the first Evaluation in a chain. */ public Evaluation getPrevious() { return previous; } /** * Returns the first child of this evaluation. Returns null if * there are no children. * * @return the first child Evaluation of the current Evaluation, or null if no children exist. */ public Evaluation getFirstChild() { return firstChild; } /** * Returns the last child of this evaluation. Returns null if * there are no children. * * @return the last child Evaluation of the current Evaluation, or null if no children exist. */ public Evaluation getLastChild() { return lastChild; } /** * Gets the first descendent. In any Evaluation tree this will the * Evaluation that was first executed. * * @return the first descendant Evaluation (first Evaluation executed in the tree). */ public Evaluation getFirstDescendant() { if (firstChild != null) { return firstChild.getFirstDescendant(); } return this; } /** * Gets the last descendent. In any Evaluation tree this will the * Evaluation that was most recently executing. * * @return the last descendant Evaluation (most recent Evaluation executed in the tree). */ public Evaluation getLastDescendant() { if (lastChild != null) { return lastChild.getLastDescendant(); } return this; } /** * Adds a child to the list of children of this evaluation. The * parent of the child is set to the receiver and the children * references are modified in the receiver to reflect the new child. * The lastChild of the receiver is set to the child, and the * firstChild is set also if child is the first (or only) child. * * @param child an Evaluation to add as a child to the current Evaluation. */ public void addChild(Evaluation child) { if (firstChild == null) { firstChild = lastChild = child; } else { if (firstChild == lastChild) { firstChild.next = child; lastChild = child; lastChild.previous = firstChild; } else { child.previous = lastChild; lastChild.next = child; lastChild = child; } } child.parent = this; } /** * Reinitializes this Evaluation to the parameters specified. * * @param node a SimpleNode for this Evaluation. * @param source a source Object for this Evaluation. * @param setOperation true to identify this Evaluation as a set operation, false to identify it as a get operation. */ public void init(SimpleNode node, Object source, boolean setOperation) { this.node = node; this.source = source; this.setOperation = setOperation; result = null; exception = null; parent = null; next = null; previous = null; firstChild = null; lastChild = null; } /** * Resets this Evaluation to the initial state. */ public void reset() { init(null, null, false); } /** * Produces a String value for the Evaluation. If compact is * true then a more compact form of the description only including * the node type and unique identifier is shown, else a full * description including source and result are shown. If showChildren * is true the child evaluations are printed using the depth string * given as a prefix. * * @param compact true to generate a compact form of the description for this Evaluation, false for a full form. * @param showChildren true to generate descriptions for child Evaluation elements of this Evaluation. * @param depth prefix String to use in front of child Evaluation description output - used when showChildren is true. * @return the description of this Evaluation as a String. */ public String toString(boolean compact, boolean showChildren, String depth) { StringBuilder stringResult; if (compact) { stringResult = new StringBuilder(depth + "<" + node.getClass().getName() + " " + System.identityHashCode(this) + ">"); } else { String ss = (source != null) ? source.getClass().getName() : "null", rs = (result != null) ? result.getClass().getName() : "null"; stringResult = new StringBuilder(depth + "<" + node.getClass().getName() + ": [" + (setOperation ? "set" : "get") + "] source = " + ss + ", result = " + result + " [" + rs + "]>"); } if (showChildren) { Evaluation child = firstChild; stringResult.append("\n"); while (child != null) { stringResult.append(child.toString(compact, depth + " ")); child = child.next; } } return stringResult.toString(); } /** * Produces a String value for the Evaluation. If compact is * true then a more compact form of the description only including * the node type and unique identifier is shown, else a full * description including source and result are shown. Child * evaluations are printed using the depth string given as a prefix. * * @param compact true to generate a compact form of the description for this Evaluation, false for a full form. * @param depth prefix String to use in front of child Evaluation description output - used when showChildren is true. * @return the description of this Evaluation as a String. */ public String toString(boolean compact, String depth) { return toString(compact, true, depth); } /** * Returns a String description of the Evaluation. * * @return the description of this Evaluation as a String. */ public String toString() { return toString(false, ""); } } ================================================ FILE: ognl/src/main/java/ognl/EvaluationPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; public final class EvaluationPool> { /** * Returns an Evaluation that contains the node, source and whether it * is a set operation. If there are no Evaluation objects in the * pool one is created and returned. * * @param node a SimpleNode for an Evaluation to be created. * @param source a source Object for an Evaluation to be created. * @return an Evaluation based on the parameters. */ public Evaluation create(SimpleNode node, Object source) { return create(node, source, false); } /** * Returns an Evaluation that contains the node, source and whether it * is a set operation. * * @param node a SimpleNode for an Evaluation to be created. * @param source a source Object for an Evaluation to be created. * @param setOperation true to identify the Evaluation to be created as a set operation, false to identify it as a get operation. * @return an Evaluation based on the parameters. */ public Evaluation create(SimpleNode node, Object source, boolean setOperation) { // synchronization is removed as we do not rely anymore on the in-house object pooling return new Evaluation<>(node, source, setOperation); } } ================================================ FILE: ognl/src/main/java/ognl/ExpressionNode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import java.io.Serial; public abstract class ExpressionNode> extends SimpleNode { @Serial private static final long serialVersionUID = -5621154197602861123L; public ExpressionNode(int i) { super(i); } public ExpressionNode(OgnlParser p, int i) { super(p, i); } /** * Returns true iff this node is constant without respect to the children. */ public boolean isNodeConstant(C context) throws OgnlException { return false; } public boolean isConstant(C context) throws OgnlException { boolean result = isNodeConstant(context); if ((children != null) && (children.length > 0)) { result = true; for (int i = 0; result && (i < children.length); ++i) { if (children[i] instanceof SimpleNode) { result = ((SimpleNode) children[i]).isConstant(context); } else { result = false; } } } return result; } public String getExpressionOperator(int index) { throw new RuntimeException("unknown operator for " + OgnlParserTreeConstants.jjtNodeName[id]); } public String toString() { StringBuilder result = new StringBuilder((parent == null) ? "" : "("); if ((children != null) && (children.length > 0)) { for (int i = 0; i < children.length; ++i) { if (i > 0) { result.append(" ").append(getExpressionOperator(i)).append(" "); } result.append(children[i].toString()); } } if (parent != null) { result.append(")"); } return result.toString(); } public String toGetSourceString(C context, Object target) { StringBuilder result = new StringBuilder((parent == null || NumericExpression.class.isAssignableFrom(parent.getClass())) ? "" : "("); if ((children != null) && (children.length > 0)) { for (int i = 0; i < children.length; ++i) { if (i > 0) { result.append(" ").append(getExpressionOperator(i)).append(" "); } String value = children[i].toGetSourceString(context, target); if ((children[i] instanceof ASTProperty || children[i] instanceof ASTMethod || children[i] instanceof ASTSequence || children[i] instanceof ASTChain) && value != null && value.trim().length() > 0) { String pre = null; if (children[i] instanceof ASTMethod) { pre = (String) context.get("_currentChain"); } if (pre == null) pre = ""; String cast = (String) context.remove(ExpressionCompiler.PRE_CAST); if (cast == null) cast = ""; value = cast + ExpressionCompiler.getRootExpression(children[i], context.getRoot(), context) + pre + value; } result.append(value); } } if (parent != null && !NumericExpression.class.isAssignableFrom(parent.getClass())) { result.append(")"); } return result.toString(); } public String toSetSourceString(C context, Object target) { StringBuilder result = new StringBuilder((parent == null) ? "" : "("); if ((children != null) && (children.length > 0)) { for (int i = 0; i < children.length; ++i) { if (i > 0) { result.append(" ").append(getExpressionOperator(i)).append(" "); } result.append(children[i].toSetSourceString(context, target)); } } if (parent != null) { result.append(")"); } return result.toString(); } @Override public boolean isOperation(C context) throws OgnlException { return true; } } ================================================ FILE: ognl/src/main/java/ognl/ExpressionSyntaxException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * Exception thrown if a malformed OGNL expression is encountered. */ public class ExpressionSyntaxException extends OgnlException { private static final long serialVersionUID = 3219409775304901172L; public ExpressionSyntaxException(String expression, Throwable reason) { super("Malformed OGNL expression: " + expression, reason); } } ================================================ FILE: ognl/src/main/java/ognl/InappropriateExpressionException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * Exception thrown if an OGNL expression is evaluated in the wrong context; the usual * case is when an expression that does not end in a property reference is passed to * setValue. */ public class InappropriateExpressionException extends OgnlException { private static final long serialVersionUID = 1976942828887727759L; public InappropriateExpressionException(Node tree) { super("Inappropriate OGNL expression: " + tree); } } ================================================ FILE: ognl/src/main/java/ognl/IteratorElementsAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; import java.util.Iterator; /** * Implementation of the ElementsAccessor interface for Iterators, which simply returns * the target iterator itself. */ public class IteratorElementsAccessor implements ElementsAccessor { public Enumeration getElements(Object target) { return new IteratorEnumeration((Iterator) target); } } ================================================ FILE: ognl/src/main/java/ognl/IteratorEnumeration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; import java.util.Iterator; /** * Maps an Iterator to an Enumeration */ public class IteratorEnumeration implements Enumeration { private final Iterator it; public IteratorEnumeration(Iterator it) { super(); this.it = it; } public boolean hasMoreElements() { return it.hasNext(); } public Object nextElement() { return it.next(); } } ================================================ FILE: ognl/src/main/java/ognl/IteratorPropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Iterator; /** * Implementation of PropertyAccessor that provides "property" reference to * "next" and "hasNext". */ public class IteratorPropertyAccessor> extends ObjectPropertyAccessor implements PropertyAccessor { public Object getProperty(C context, Object target, Object name) throws OgnlException { Object result; Iterator iterator = (Iterator) target; if (name instanceof String) { if (name.equals("next")) { result = iterator.next(); } else { if (name.equals("hasNext")) { result = iterator.hasNext() ? Boolean.TRUE : Boolean.FALSE; } else { result = super.getProperty(context, target, name); } } } else { result = super.getProperty(context, target, name); } return result; } public void setProperty(C context, Object target, Object name, Object value) throws OgnlException { throw new IllegalArgumentException("can't set property " + name + " on Iterator"); } } ================================================ FILE: ognl/src/main/java/ognl/JJTOgnlParserState.java ================================================ /* Generated By:JavaCC: Do not edit this line. JJTOgnlParserState.java Version 4.1d1 */ package ognl; public class JJTOgnlParserState { private java.util.List nodes; private java.util.List marks; private int sp; // number of nodes on stack private int mk; // current mark private boolean node_created; public JJTOgnlParserState() { nodes = new java.util.ArrayList(); marks = new java.util.ArrayList(); sp = 0; mk = 0; } /* Determines whether the current node was actually closed and pushed. This should only be called in the final user action of a node scope. */ public boolean nodeCreated() { return node_created; } /* Call this to reinitialize the node stack. It is called automatically by the parser's ReInit() method. */ public void reset() { nodes.clear(); marks.clear(); sp = 0; mk = 0; } /* Returns the root node of the AST. It only makes sense to call this after a successful parse. */ public Node rootNode() { return (Node) nodes.get(0); } /* Pushes a node on to the stack. */ public void pushNode(Node n) { nodes.add(n); ++sp; } /* Returns the node on the top of the stack, and remove it from the stack. */ public Node popNode() { if (--sp < mk) { mk = ((Integer) marks.remove(marks.size() - 1)).intValue(); } return (Node) nodes.remove(nodes.size() - 1); } /* Returns the node currently on the top of the stack. */ public Node peekNode() { return (Node) nodes.get(nodes.size() - 1); } /* Returns the number of children on the stack in the current node scope. */ public int nodeArity() { return sp - mk; } public void clearNodeScope(Node n) { while (sp > mk) { popNode(); } mk = ((Integer) marks.remove(marks.size() - 1)).intValue(); } public void openNodeScope(Node n) { marks.add(mk); mk = sp; n.jjtOpen(); } /* A definite node is constructed from a specified number of children. That number of nodes are popped from the stack and made the children of the definite node. Then the definite node is pushed on to the stack. */ public void closeNodeScope(Node n, int num) { mk = ((Integer) marks.remove(marks.size() - 1)).intValue(); while (num-- > 0) { Node c = popNode(); c.jjtSetParent(n); n.jjtAddChild(c, num); } n.jjtClose(); pushNode(n); node_created = true; } /* A conditional node is constructed if its condition is true. All the nodes that have been pushed since the node was opened are made children of the conditional node, which is then pushed on to the stack. If the condition is false the node is not constructed and they are left on the stack. */ public void closeNodeScope(Node n, boolean condition) { if (condition) { int a = nodeArity(); mk = ((Integer) marks.remove(marks.size() - 1)).intValue(); while (a-- > 0) { Node c = popNode(); c.jjtSetParent(n); n.jjtAddChild(c, a); } n.jjtClose(); pushNode(n); node_created = true; } else { mk = ((Integer) marks.remove(marks.size() - 1)).intValue(); node_created = false; } } } /* JavaCC - OriginalChecksum=61071c68a05e7c9104307c34a2e37165 (do not edit this line) */ ================================================ FILE: ognl/src/main/java/ognl/JavaSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionAccessor; /** * Defines an object that can return a representation of itself and any objects it contains * in the form of a {@link String} embedded with literal java statements. * * @author jkuhnert */ public interface JavaSource> { /** * Expected to return a java source representation of itself such that * it could be turned into a literal java expression to be compiled and * executed for {@link ExpressionAccessor#get(OgnlContext, Object)} calls. * * @param context the OgnlContext within which to perform the operation. * @param target the Object from which to retrieve the get source string. * @return Literal java string representation of an object get. */ String toGetSourceString(C context, Object target); /** * Expected to return a java source representation of itself such that * it could be turned into a literal java expression to be compiled and * executed for {@link ExpressionAccessor#get(OgnlContext, Object)} calls. * * @param context the OgnlContext within which to perform the operation. * @param target the Object from which to retrieve the set source string. * @return Literal java string representation of an object set. */ String toSetSourceString(C context, Object target); } ================================================ FILE: ognl/src/main/java/ognl/ListPropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * Implementation of PropertyAccessor that uses numbers and dynamic subscripts as properties to * index into Lists. */ public class ListPropertyAccessor> extends ObjectPropertyAccessor implements PropertyAccessor { public Object getProperty(C context, Object target, Object name) throws OgnlException { List list = (List) target; if (name instanceof String) { Object result; if (name.equals("size")) { result = list.size(); } else { if (name.equals("iterator")) { result = list.iterator(); } else { if (name.equals("isEmpty") || name.equals("empty")) { result = list.isEmpty() ? Boolean.TRUE : Boolean.FALSE; } else { result = super.getProperty(context, target, name); } } } return result; } if (name instanceof Number) return list.get(((Number) name).intValue()); if (name instanceof DynamicSubscript) { int len = list.size(); switch (((DynamicSubscript) name).getFlag()) { case DynamicSubscript.FIRST: return len > 0 ? list.get(0) : null; case DynamicSubscript.MID: return len > 0 ? list.get(len / 2) : null; case DynamicSubscript.LAST: return len > 0 ? list.get(len - 1) : null; case DynamicSubscript.ALL: return new ArrayList<>(list); } } throw new NoSuchPropertyException(target, name); } public void setProperty(C context, Object target, Object name, Object value) throws OgnlException { if (name instanceof String && !((String) name).contains("$")) { super.setProperty(context, target, name, value); return; } List list = (List) target; if (name instanceof Number) { list.set(((Number) name).intValue(), value); return; } if (name instanceof DynamicSubscript) { int len = list.size(); switch (((DynamicSubscript) name).getFlag()) { case DynamicSubscript.FIRST: if (len > 0) list.set(0, value); return; case DynamicSubscript.MID: if (len > 0) list.set(len / 2, value); return; case DynamicSubscript.LAST: if (len > 0) list.set(len - 1, value); return; case DynamicSubscript.ALL: { if (!(value instanceof Collection)) throw new OgnlException("Value must be a collection"); list.clear(); list.addAll((Collection) value); return; } } } throw new NoSuchPropertyException(target, name); } public Class getPropertyClass(C context, Object target, Object index) { if (index instanceof String) { String indexStr = (String) index; String key = (indexStr.indexOf('"') >= 0 ? indexStr.replaceAll("\"", "") : indexStr); if (key.equals("size")) { return int.class; } else { if (key.equals("iterator")) { return Iterator.class; } else { if (key.equals("isEmpty") || key.equals("empty")) { return boolean.class; } else { return super.getPropertyClass(context, target, index); } } } } if (index instanceof Number) return Object.class; return null; } public String getSourceAccessor(C context, Object target, Object index) { String indexStr = index.toString(); if (indexStr.indexOf('"') >= 0) indexStr = indexStr.replaceAll("\"", ""); if (index instanceof String) { if (indexStr.equals("size")) { context.setCurrentAccessor(List.class); context.setCurrentType(int.class); return ".size()"; } else { if (indexStr.equals("iterator")) { context.setCurrentAccessor(List.class); context.setCurrentType(Iterator.class); return ".iterator()"; } else { if (indexStr.equals("isEmpty") || indexStr.equals("empty")) { context.setCurrentAccessor(List.class); context.setCurrentType(boolean.class); return ".isEmpty()"; } } } } // TODO: This feels really inefficient, must be some better way // check if the index string represents a method on a custom class implementing java.util.List instead.. if (context.getCurrentObject() != null && !(context.getCurrentObject() instanceof Number)) { try { Method m = OgnlRuntime.getReadMethod(target.getClass(), indexStr); if (m != null) { return super.getSourceAccessor(context, target, index); } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } context.setCurrentAccessor(List.class); // need to convert to primitive for list index access // System.out.println("Curent type: " + context.getCurrentType() + " current object type " + context.getCurrentObject().getClass()); if (!context.getCurrentType().isPrimitive() && Number.class.isAssignableFrom(context.getCurrentType())) { indexStr += "." + OgnlRuntime.getNumericValueGetter(context.getCurrentType()); } else if (context.getCurrentObject() != null && Number.class.isAssignableFrom(context.getCurrentObject().getClass()) && !context.getCurrentType().isPrimitive()) { // means it needs to be cast first as well String toString = index instanceof String && context.getCurrentType() != Object.class ? "" : ".toString()"; indexStr = "ognl.OgnlOps#getIntValue(" + indexStr + toString + ")"; } context.setCurrentType(Object.class); return ".get(" + indexStr + ")"; } public String getSourceSetter(C context, Object target, Object index) { String indexStr = index.toString(); if (indexStr.indexOf('"') >= 0) indexStr = indexStr.replaceAll("\"", ""); // TODO: This feels really inefficient, must be some better way // check if the index string represents a method on a custom class implementing java.util.List instead.. /* System.out.println("Listpropertyaccessor setter using index: " + index + " and current object: " + context.getCurrentObject() + " number is current object? " + Number.class.isInstance(context.getCurrentObject()));*/ if (context.getCurrentObject() != null && !(context.getCurrentObject() instanceof Number)) { try { Method m = OgnlRuntime.getWriteMethod(target.getClass(), indexStr); if (m != null || !context.getCurrentType().isPrimitive()) { // System.out.println("super source setter returned: " + super.getSourceSetter(context, target, index)); return super.getSourceSetter(context, target, index); } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } context.setCurrentAccessor(List.class); // need to convert to primitive for list index access if (!context.getCurrentType().isPrimitive() && Number.class.isAssignableFrom(context.getCurrentType())) { indexStr += "." + OgnlRuntime.getNumericValueGetter(context.getCurrentType()); } else if (context.getCurrentObject() != null && Number.class.isAssignableFrom(context.getCurrentObject().getClass()) && !context.getCurrentType().isPrimitive()) { // means it needs to be cast first as well String toString = index instanceof String && context.getCurrentType() != Object.class ? "" : ".toString()"; indexStr = "ognl.OgnlOps#getIntValue(" + indexStr + toString + ")"; } context.setCurrentType(Object.class); return ".set(" + indexStr + ", $3)"; } } ================================================ FILE: ognl/src/main/java/ognl/MapElementsAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; import java.util.Map; /** * Implementation of ElementsAccessor that returns an iterator over the map's values. */ public class MapElementsAccessor implements ElementsAccessor { public Enumeration getElements(Object target) { return new IteratorEnumeration(((Map) target).values().iterator()); } } ================================================ FILE: ognl/src/main/java/ognl/MapPropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Collection; import java.util.Map; import java.util.Set; /** * Implementation of PropertyAccessor that sets and gets properties by storing and looking up values * in Maps. */ public class MapPropertyAccessor> implements PropertyAccessor { public Object getProperty(C context, Object target, Object name) throws OgnlException { Object result; Map map = (Map) target; Node currentNode = context.getCurrentNode().jjtGetParent(); boolean indexedAccess = false; if (currentNode == null) { throw new OgnlException("node is null for '" + name + "'"); } if (!(currentNode instanceof ASTProperty)) { currentNode = currentNode.jjtGetParent(); } if (currentNode instanceof ASTProperty) { indexedAccess = ((ASTProperty) currentNode).isIndexedAccess(); } if ((name instanceof String) && !indexedAccess) { if (name.equals("size")) { result = map.size(); } else { if (name.equals("keys") || name.equals("keySet")) { result = map.keySet(); } else { if (name.equals("values")) { result = map.values(); } else { if (name.equals("isEmpty")) { result = map.isEmpty() ? Boolean.TRUE : Boolean.FALSE; } else { result = map.get(name); } } } } } else { result = map.get(name); } return result; } public void setProperty(C context, Object target, Object name, Object value) throws OgnlException { Map map = (Map) target; map.put(name, value); } public String getSourceAccessor(C context, Object target, Object index) { Node currentNode = context.getCurrentNode().jjtGetParent(); boolean indexedAccess = false; if (currentNode == null) throw new RuntimeException("node is null for '" + index + "'"); if (!(currentNode instanceof ASTProperty)) currentNode = currentNode.jjtGetParent(); if (currentNode instanceof ASTProperty) indexedAccess = ((ASTProperty) currentNode).isIndexedAccess(); String indexStr = index.toString(); context.setCurrentAccessor(Map.class); context.setCurrentType(Object.class); if (index instanceof String && !indexedAccess) { String key = (indexStr.indexOf('"') >= 0 ? indexStr.replaceAll("\"", "") : indexStr); switch (key) { case "size": context.setCurrentType(int.class); return ".size()"; case "keys": case "keySet": context.setCurrentType(Set.class); return ".keySet()"; case "values": context.setCurrentType(Collection.class); return ".values()"; case "isEmpty": context.setCurrentType(boolean.class); return ".isEmpty()"; } } return ".get(" + indexStr + ")"; } public String getSourceSetter(C context, Object target, Object index) { context.setCurrentAccessor(Map.class); context.setCurrentType(Object.class); String indexStr = index.toString(); if (index instanceof String) { String key = (indexStr.indexOf('"') >= 0 ? indexStr.replaceAll("\"", "") : indexStr); switch (key) { case "size": return ""; case "keys": case "keySet": return ""; case "values": return ""; case "isEmpty": return ""; } } return ".put(" + indexStr + ", $3)"; } } ================================================ FILE: ognl/src/main/java/ognl/MemberAccess.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Member; /** * This interface provides a hook for preparing for accessing members * of objects. The Java2 version of this method can allow access * to otherwise inaccessible members, such as private fields. */ public interface MemberAccess> { /** * Sets the member up for accessibility * * @param context the current execution context. * @param target the Object upon which to perform the setup operation. * @param member the Member upon which to perform the setup operation. * @param propertyName the property upon which to perform the setup operation. * @return the Object representing the original accessibility state of the target prior to the setup operation. */ Object setup(C context, Object target, Member member, String propertyName); /** * Restores the member from the previous setup call. * * @param context the current execution context. * @param target the Object upon which to perform the setup operation. * @param member the Member upon which to perform the setup operation. * @param propertyName the property upon which to perform the setup operation. * @param state the Object holding the state to restore (target state prior to the setup operation). */ void restore(C context, Object target, Member member, String propertyName, Object state); /** * Returns true if the given member is accessible or can be made accessible * by this object. * * @param context the current execution context. * @param target the Object to test accessibility for. * @param member the Member to test accessibility for. * @param propertyName the property to test accessibility for. * @return true if the target/member/propertyName is accessible in the context, false otherwise. */ boolean isAccessible(C context, Object target, Member member, String propertyName); } ================================================ FILE: ognl/src/main/java/ognl/MethodAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * This interface defines methods for calling methods in a target object. * Methods are broken up into static and instance methods for convenience. * indexes into the target object, which must be an array. */ public interface MethodAccessor> { /** * Calls the static method named with the arguments given on the class given. * * @param context expression context in which the method should be called * @param targetClass the object in which the method exists * @param methodName the name of the method * @param args the arguments to the method * @return result of calling the method * @throws MethodFailedException if there is an error calling the method */ Object callStaticMethod(C context, Class targetClass, String methodName, Object[] args) throws MethodFailedException; /** * Calls the method named with the arguments given. * * @param context expression context in which the method should be called * @param target the object in which the method exists * @param methodName the name of the method * @param args the arguments to the method * @return result of calling the method * @throws MethodFailedException if there is an error calling the method */ Object callMethod(C context, Object target, String methodName, Object[] args) throws MethodFailedException; } ================================================ FILE: ognl/src/main/java/ognl/MethodFailedException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * Exception thrown if a method or constructor call fails. */ public class MethodFailedException extends OgnlException { private static final long serialVersionUID = 2490616172311289862L; public MethodFailedException(Object source, String name) { super("Method \"" + name + "\" failed for object " + source); } public MethodFailedException(Object source, String name, Throwable reason) { super("Method \"" + name + "\" failed for object " + source, reason); } } ================================================ FILE: ognl/src/main/java/ognl/NoSuchPropertyException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * Exception thrown if a property is attempted to be extracted from an object that does * not have such a property.* */ public class NoSuchPropertyException extends OgnlException { private static final long serialVersionUID = -4394252641910997725L; private Object target; private Object name; public NoSuchPropertyException(Object target, Object name) { super(getReason(target, name)); } public NoSuchPropertyException(Object target, Object name, Throwable reason) { super(getReason(target, name), reason); this.target = target; this.name = name; } static String getReason(Object target, Object name) { String ret; if (target == null) ret = "null"; else if (target instanceof Class) ret = ((Class) target).getName(); else ret = target.getClass().getName(); ret += "." + name; return ret; } public Object getTarget() { return target; } public Object getName() { return name; } } ================================================ FILE: ognl/src/main/java/ognl/Node.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionAccessor; /** * JJTree interface for AST nodes, as modified to handle the OGNL operations getValue and * setValue. JJTree's original comment: *

* All AST nodes must implement this interface. It provides basic * machinery for constructing the parent and child relationships * between nodes. */ public interface Node> extends JavaSource { /** * This method is called after the node has been made the current * node. It indicates that child nodes can now be added to it. */ void jjtOpen(); /** * This method is called after all the child nodes have been * added. */ void jjtClose(); /** * This pair of methods are used to inform the node of its * parent. * * @param n the Node to make the parent of this node. */ void jjtSetParent(Node n); Node jjtGetParent(); /** * This method tells the node to add its argument to the node's * list of children. * * @param n the Node to add as a child of this node. * @param i the position at which to add the child node. */ void jjtAddChild(Node n, int i); /** * This method returns a child node. The children are numbered * from zero, left to right. * * @param i the position from which to get the child node. * @return the child Node at position i. */ Node jjtGetChild(int i); /** * Return the number of children the node has. * * @return the number of children for this node. */ int jjtGetNumChildren(); // OGNL additions to Node: /** * Extracts the value from the given source object that is appropriate for this node * within the given context. * * @param context the OgnlContext within which to perform the operation. * @param source the Object from which to get the value. * @return the value from the source (as appropriate within the provided context). * @throws OgnlException if the value get fails. */ Object getValue(C context, Object source) throws OgnlException; /** * Sets the given value in the given target as appropriate for this node within the * given context. * * @param context the OgnlContext within which to perform the operation. * @param target the Object upon which to set the value. * @param value the Object representing the value to apply to the target. * @throws OgnlException if the value set fails. */ void setValue(C context, Object target, Object value) throws OgnlException; /** * Gets the compiled bytecode enhanced expression accessor for getting/setting values. * * @return The accessor for this node, or null if none has been compiled for it. */ ExpressionAccessor getAccessor(); /** * Sets a new compiled accessor for this node expression. * * @param accessor The compiled representation of this node. */ void setAccessor(ExpressionAccessor accessor); } ================================================ FILE: ognl/src/main/java/ognl/NodeType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.OgnlExpressionCompiler; /** * Used by some of the {@link OgnlExpressionCompiler} logic to determine the object * type of {@link Node}s during expression evaluation. */ public interface NodeType { /** * The type returned from the expression - if any. * * @return The type. */ Class getGetterClass(); /** * The type used to set the value - if any. * * @return The type. */ Class getSetterClass(); } ================================================ FILE: ognl/src/main/java/ognl/NullHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * Interface for handling null results from Chains. * Object has the opportunity to substitute an object for the * null and continue. */ public interface NullHandler> { /** * Method called on target returned null. * * @param context the current execution context. * @param target the Object on which the method was called. * @param methodName the name of the method which was called. * @param args the arguments to the method that was called. * @return the result Object containing the state of the method call that returned null. */ Object nullMethodResult(C context, Object target, String methodName, Object[] args); /** * Property in target evaluated to null. Property can be a constant * String property name or a DynamicSubscript. * * @param context the current execution context. * @param target the Object to which the property belongs. * @param property the property whose value evaluated to null. * @return the result Object containing the state of the property that evaluated to null. */ Object nullPropertyValue(C context, Object target, Object property); } ================================================ FILE: ognl/src/main/java/ognl/NumberElementsAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; import java.util.NoSuchElementException; /** * Implementation of ElementsAccessor that returns an iterator over integers from 0 up to * the given target. */ public class NumberElementsAccessor implements ElementsAccessor, NumericTypes { public Enumeration getElements(final Object target) { return new Enumeration<>() { private final int type = OgnlOps.getNumericType(target); private final long finish = OgnlOps.longValue(target); private long next = 0; public boolean hasMoreElements() { return next < finish; } public Object nextElement() { if (next >= finish) throw new NoSuchElementException(); return OgnlOps.newInteger(type, next++); } }; } } ================================================ FILE: ognl/src/main/java/ognl/NumericCasts.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; /** * Constant strings for casting different primitive types. */ class NumericCasts { private final Map, String> NUMERIC_CASTS = new HashMap<>(6); NumericCasts() { NUMERIC_CASTS.put(Double.class, "(double)"); NUMERIC_CASTS.put(Float.class, "(float)"); NUMERIC_CASTS.put(Integer.class, "(int)"); NUMERIC_CASTS.put(Long.class, "(long)"); NUMERIC_CASTS.put(BigDecimal.class, "(double)"); NUMERIC_CASTS.put(BigInteger.class, ""); } String get(Class cls) { return NUMERIC_CASTS.get(cls); } } ================================================ FILE: ognl/src/main/java/ognl/NumericDefaults.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; class NumericDefaults { private final Map, Object> NUMERIC_DEFAULTS = new HashMap<>(10); NumericDefaults() { NUMERIC_DEFAULTS.put(Boolean.class, Boolean.FALSE); NUMERIC_DEFAULTS.put(Byte.class, (byte) 0); NUMERIC_DEFAULTS.put(Short.class, (short) 0); NUMERIC_DEFAULTS.put(Character.class, (char) 0); NUMERIC_DEFAULTS.put(Integer.class, 0); NUMERIC_DEFAULTS.put(Long.class, 0L); NUMERIC_DEFAULTS.put(Float.class, 0.0f); NUMERIC_DEFAULTS.put(Double.class, 0.0); NUMERIC_DEFAULTS.put(BigInteger.class, BigInteger.ZERO); NUMERIC_DEFAULTS.put(BigDecimal.class, BigDecimal.ZERO); } Object get(Class cls) { return NUMERIC_DEFAULTS.get(cls); } } ================================================ FILE: ognl/src/main/java/ognl/NumericExpression.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import java.io.Serial; /** * Base class for numeric expressions. */ public abstract class NumericExpression> extends ExpressionNode implements NodeType { @Serial private static final long serialVersionUID = 2899128497573777569L; protected Class getterClass; public NumericExpression(int id) { super(id); } public NumericExpression(OgnlParser p, int id) { super(p, id); } public Class getGetterClass() { if (getterClass != null) return getterClass; return Double.TYPE; } public Class getSetterClass() { return null; } public String toGetSourceString(C context, Object target) { Object value; StringBuilder result = new StringBuilder(); try { value = getValueBody(context, target); if (value != null) { getterClass = value.getClass(); } for (int i = 0; i < children.length; i++) { if (i > 0) { result.append(" ").append(getExpressionOperator(i)).append(" "); } String str = OgnlRuntime.getChildSource(context, target, children[i]); result.append(coerceToNumeric(str, context, children[i])); } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return result.toString(); } public String coerceToNumeric(String source, C context, Node child) { String ret = source; Object value = context.getCurrentObject(); if (child instanceof ASTConst && value != null) { String literal = OgnlRuntime.getNumericLiteral(value.getClass()); return value.toString() + (literal != null ? literal : ""); } if (context.getCurrentType() != null && !context.getCurrentType().isPrimitive() && context.getCurrentObject() != null && context.getCurrentObject() instanceof Number) { ret = "((" + ExpressionCompiler.getCastString(context.getCurrentObject().getClass()) + ")" + ret + ")"; ret += "." + OgnlRuntime.getNumericValueGetter(context.getCurrentObject().getClass()); } else if (context.getCurrentType() != null && context.getCurrentType().isPrimitive() && (child instanceof ASTConst || child instanceof NumericExpression)) { ret += OgnlRuntime.getNumericLiteral(context.getCurrentType()); } else if (context.getCurrentType() != null && String.class.isAssignableFrom(context.getCurrentType())) { ret = "Double.parseDouble(" + ret + ")"; context.setCurrentType(Double.TYPE); } if (child instanceof NumericExpression) ret = "(" + ret + ")"; return ret; } } ================================================ FILE: ognl/src/main/java/ognl/NumericLiterals.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; /** * Numeric primitive literal string expressions. */ class NumericLiterals { private final Map, String> NUMERIC_LITERALS = new HashMap<>(10); NumericLiterals() { NUMERIC_LITERALS.put(Integer.class, ""); NUMERIC_LITERALS.put(Integer.TYPE, ""); NUMERIC_LITERALS.put(Long.class, "l"); NUMERIC_LITERALS.put(Long.TYPE, "l"); NUMERIC_LITERALS.put(Float.class, "f"); NUMERIC_LITERALS.put(Float.TYPE, "f"); NUMERIC_LITERALS.put(Double.class, "d"); NUMERIC_LITERALS.put(Double.TYPE, "d"); NUMERIC_LITERALS.put(BigInteger.class, "d"); NUMERIC_LITERALS.put(BigDecimal.class, "d"); } String get(Class clazz) { return NUMERIC_LITERALS.get(clazz); } } ================================================ FILE: ognl/src/main/java/ognl/NumericTypes.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * This interface defines some useful constants for describing the various possible * numeric types of OGNL. */ public interface NumericTypes { // Order does matter here... see the getNumericType methods in ognl.g. /** * Type tag meaning boolean. */ int BOOL = 0; /** * Type tag meaning byte. */ int BYTE = 1; /** * Type tag meaning char. */ int CHAR = 2; /** * Type tag meaning short. */ int SHORT = 3; /** * Type tag meaning int. */ int INT = 4; /** * Type tag meaning long. */ int LONG = 5; /** * Type tag meaning java.math.BigInteger. */ int BIGINT = 6; /** * Type tag meaning float. */ int FLOAT = 7; /** * Type tag meaning double. */ int DOUBLE = 8; /** * Type tag meaning java.math.BigDecimal. */ int BIGDEC = 9; /** * Type tag meaning something other than a number. */ int NONNUMERIC = 10; /** * The smallest type tag that represents reals as opposed to integers. You can see * whether a type tag represents reals or integers by comparing the tag to this * constant: all tags less than this constant represent integers, and all tags * greater than or equal to this constant represent reals. Of course, you must also * check for NONNUMERIC, which means it is not a number at all. */ int MIN_REAL_TYPE = FLOAT; } ================================================ FILE: ognl/src/main/java/ognl/NumericValues.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; /** * Constant strings for getting the primitive value of different native types on the generic {@link Number} object * interface. (or the less generic BigDecimal/BigInteger types) */ class NumericValues { private final Map, String> NUMERIC_VALUES = new HashMap<>(9); NumericValues() { NUMERIC_VALUES.put(Double.class, "doubleValue()"); NUMERIC_VALUES.put(Float.class, "floatValue()"); NUMERIC_VALUES.put(Integer.class, "intValue()"); NUMERIC_VALUES.put(Long.class, "longValue()"); NUMERIC_VALUES.put(Short.class, "shortValue()"); NUMERIC_VALUES.put(Byte.class, "byteValue()"); NUMERIC_VALUES.put(BigDecimal.class, "doubleValue()"); NUMERIC_VALUES.put(BigInteger.class, "doubleValue()"); NUMERIC_VALUES.put(Boolean.class, "booleanValue()"); } String get(Class cls) { return NUMERIC_VALUES.get(cls); } } ================================================ FILE: ognl/src/main/java/ognl/ObjectElementsAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Enumeration; /** * Implementation of ElementsAccessor that returns a single-element iterator, containing * the original target object. */ public class ObjectElementsAccessor implements ElementsAccessor { public Enumeration getElements(Object target) { final Object object = target; return new Enumeration<>() { private boolean seen = false; public boolean hasMoreElements() { return !seen; } public Object nextElement() { Object result = null; if (!seen) { result = object; seen = true; } return result; } }; } } ================================================ FILE: ognl/src/main/java/ognl/ObjectIndexedPropertyDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; /** *

PropertyDescriptor subclass that describes an indexed set of read/write * methods to get a property. Unlike IndexedPropertyDescriptor this allows * the "key" to be an arbitrary object rather than just an int. Consequently * it does not have a "readMethod" or "writeMethod" because it only expects * a pattern like:

*
 *    public void setProperty(KeyType, ValueType);
 *    public ValueType getProperty(KeyType);
 * 
*

and does not require the methods that access it as an array. OGNL can * get away with this without losing functionality because if the object * does expose the properties they are most probably in a Map and that case * is handled by the normal OGNL property accessors. *

*

For example, if an object were to have methods that accessed and "attributes" * property it would be natural to index them by String rather than by integer * and expose the attributes as a map with a different property name: *

 *    public void setAttribute(String name, Object value);
 *    public Object getAttribute(String name);
 *    public Map getAttributes();
 * 
*

Note that the index get/set is called get/set Attribute * whereas the collection getter is called Attributes. This * case is handled unambiguously by the OGNL property accessors because the * set/getAttribute methods are detected by this object and the * "attributes" case is handled by the MapPropertyAccessor. * Therefore OGNL expressions calling this code would be handled in the * following way: *

* * * * * * * * * * * * * * * * * * * * * *
OGNL Expression
OGNL ExpressionHandling
attribute["name"]Handled by an index getter, like getAttribute(String).
attribute["name"] = valueHandled by an index setter, like setAttribute(String, Object).
attributes["name"]Handled by MapPropertyAccessor via a Map.get(). This * will not go through the index get accessor. *
attributes["name"] = valueHandled by MapPropertyAccessor via a Map.put(). This * will not go through the index set accessor. *
*/ public class ObjectIndexedPropertyDescriptor extends PropertyDescriptor { private final Method indexedReadMethod; private final Method indexedWriteMethod; private final Class propertyType; public ObjectIndexedPropertyDescriptor(String propertyName, Class propertyType, Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException { super(propertyName, null, null); this.propertyType = propertyType; this.indexedReadMethod = indexedReadMethod; this.indexedWriteMethod = indexedWriteMethod; } public Method getIndexedReadMethod() { return indexedReadMethod; } public Method getIndexedWriteMethod() { return indexedWriteMethod; } public synchronized Class getPropertyType() { return propertyType; } } ================================================ FILE: ognl/src/main/java/ognl/ObjectMethodAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Method; import java.util.List; /** * Implementation of PropertyAccessor that uses reflection on the target object's class to find a * field or a pair of set/get methods with the given property name. */ public class ObjectMethodAccessor> implements MethodAccessor { public Object callStaticMethod(C context, Class targetClass, String methodName, Object[] args) throws MethodFailedException { List methods = OgnlRuntime.getMethods(targetClass, methodName, true); return OgnlRuntime.callAppropriateMethod(context, targetClass, null, methodName, null, methods, args); } public Object callMethod(C context, Object target, String methodName, Object[] args) throws MethodFailedException { Class targetClass = (target == null) ? null : target.getClass(); List methods = OgnlRuntime.getMethods(targetClass, methodName, false); if ((methods == null) || (methods.isEmpty())) { methods = OgnlRuntime.getMethods(targetClass, methodName, true); } return OgnlRuntime.callAppropriateMethod(context, target, target, methodName, null, methods, args); } } ================================================ FILE: ognl/src/main/java/ognl/ObjectNullHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * Implementation of NullHandler that returns null in all cases, * so that NullPointerException will be thrown by the caller. */ public class ObjectNullHandler> implements NullHandler { public Object nullMethodResult(C context, Object target, String methodName, Object[] args) { return null; } public Object nullPropertyValue(C context, Object target, Object property) { return null; } } ================================================ FILE: ognl/src/main/java/ognl/ObjectPropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.UnsupportedCompilationException; import java.beans.IntrospectionException; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Implementation of PropertyAccessor that uses reflection on the target object's class to find a * field or a pair of set/get methods with the given property name. */ public class ObjectPropertyAccessor> implements PropertyAccessor { /** * Returns OgnlRuntime.NotFound if the property does not exist. * * @param context the current execution context. * @param target the object to get the property from. * @param name the name of the property to get. * @return the current value of the given property in the given object. * @throws OgnlException if there is an error locating the property in the given object. */ public Object getPossibleProperty(C context, Object target, String name) throws OgnlException { Object result; try { if ((result = OgnlRuntime.getMethodValue(context, target, name, true)) == OgnlRuntime.NotFound) { result = OgnlRuntime.getFieldValue(context, target, name, true); } } catch (OgnlException ex) { throw ex; } catch (Exception ex) { throw new OgnlException(name, ex); } return result; } /** * Returns OgnlRuntime.NotFound if the property does not exist. * * @param context the current execution context. * @param target the object to set the property in. * @param name the name of the property to set. * @param value the new value for the property. * @return the Object result of the property set operation. * @throws OgnlException if there is an error setting the property in the given object. */ public Object setPossibleProperty(C context, Object target, String name, Object value) throws OgnlException { Object result = null; try { if (!OgnlRuntime.setMethodValue(context, target, name, value, true)) { result = OgnlRuntime.setFieldValue(context, target, name, value, true) ? null : OgnlRuntime.NotFound; } if (result == OgnlRuntime.NotFound) { Method m = OgnlRuntime.getWriteMethod(target.getClass(), name); if (m != null && context.getMemberAccess().isAccessible(context, target, m, name)) { result = m.invoke(target, value); } } } catch (OgnlException ex) { throw ex; } catch (Exception ex) { throw new OgnlException(name, ex); } return result; } public boolean hasGetProperty(C context, Object target, Object oname) throws OgnlException { try { return OgnlRuntime.hasGetProperty(context, target, oname); } catch (IntrospectionException ex) { throw new OgnlException("checking if " + target + " has gettable property " + oname, ex); } } public boolean hasSetProperty(C context, Object target, Object oname) throws OgnlException { try { return OgnlRuntime.hasSetProperty(context, target, oname); } catch (IntrospectionException ex) { throw new OgnlException("checking if " + target + " has settable property " + oname, ex); } } public Object getProperty(C context, Object target, Object oname) throws OgnlException { String name = oname.toString(); Object result = getPossibleProperty(context, target, name); if (result == OgnlRuntime.NotFound) { throw new NoSuchPropertyException(target, name); } return result; } public void setProperty(C context, Object target, Object oname, Object value) throws OgnlException { String name = oname.toString(); Object result = setPossibleProperty(context, target, name, value); if (result == OgnlRuntime.NotFound) { throw new NoSuchPropertyException(target, name); } } public Class getPropertyClass(C context, Object target, Object index) { try { Method m = OgnlRuntime.getReadMethod(target.getClass(), index.toString()); if (m == null) { if (String.class.isAssignableFrom(index.getClass()) && !target.getClass().isArray()) { String indexStr = (String) index; String key = (indexStr.indexOf('"') >= 0) ? indexStr.replaceAll("\"", "") : indexStr; try { Field f = target.getClass().getField(key); return f.getType(); } catch (NoSuchFieldException e) { return null; } } return null; } return m.getReturnType(); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } public String getSourceAccessor(C context, Object target, Object index) { try { String indexStr = index.toString(); String methodName = (indexStr.indexOf('"') >= 0 ? indexStr.replaceAll("\"", "") : indexStr); Method m = OgnlRuntime.getReadMethod(target.getClass(), methodName); // try last ditch effort of checking if they were trying to do reflection via a return method value if (m == null && context.getCurrentObject() != null) { String currentObjectStr = context.getCurrentObject().toString(); m = OgnlRuntime.getReadMethod(target.getClass(), (currentObjectStr.indexOf('"') >= 0 ? currentObjectStr.replaceAll("\"", "") : currentObjectStr)); } if (m == null) { try { if (String.class.isAssignableFrom(index.getClass()) && !target.getClass().isArray()) { Field f = target.getClass().getField(methodName); context.setCurrentType(f.getType()); context.setCurrentAccessor(f.getDeclaringClass()); return "." + f.getName(); } } catch (NoSuchFieldException e) { // ignore } return ""; } context.setCurrentType(m.getReturnType()); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); return "." + m.getName() + "()"; } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } public String getSourceSetter(C context, Object target, Object index) { try { String indexStr = index.toString(); String methodName = (indexStr.indexOf('"') >= 0 ? indexStr.replaceAll("\"", "") : indexStr); Method m = OgnlRuntime.getWriteMethod(target.getClass(), methodName); if (m == null && context.getCurrentObject() != null && context.getCurrentObject().toString() != null) { String currentObjectStr = context.getCurrentObject().toString(); m = OgnlRuntime.getWriteMethod(target.getClass(), (currentObjectStr.indexOf('"') >= 0 ? currentObjectStr.replaceAll("\"", "") : currentObjectStr)); } if (m == null || m.getParameterTypes().length <= 0) { throw new UnsupportedCompilationException("Unable to determine setting expression on " + context.getCurrentObject() + " with index of " + index); } Class param = m.getParameterTypes()[0]; String conversion; if (m.getParameterTypes().length > 1) throw new UnsupportedCompilationException("Object property accessors can only support single parameter setters."); if (param.isPrimitive()) { Class wrapClass = OgnlRuntime.getPrimitiveWrapperClass(param); conversion = OgnlRuntime.getCompiler().createLocalReference(context, "((" + wrapClass.getName() + ")ognl.OgnlOps#convertValue($3," + wrapClass.getName() + ".class, true))." + OgnlRuntime.getNumericValueGetter(wrapClass), param); } else if (param.isArray()) { conversion = OgnlRuntime.getCompiler().createLocalReference(context, "(" + ExpressionCompiler.getCastString(param) + ")ognl.OgnlOps#toArray($3," + param.getComponentType().getName() + ".class)", param); } else { conversion = OgnlRuntime.getCompiler().createLocalReference(context, "(" + param.getName() + ")ognl.OgnlOps#convertValue($3," + param.getName() + ".class)", param); } context.setCurrentType(m.getReturnType()); context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); return "." + m.getName() + "(" + conversion + ")"; } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } } } ================================================ FILE: ognl/src/main/java/ognl/Ognl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionAccessor; import ognl.enhance.OgnlExpressionCompiler; import java.io.StringReader; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; /** * This class provides static methods for parsing and interpreting OGNL expressions. * The simplest use of the Ognl class is to get the value of an expression from an object, without * extra context or pre-parsing. *
 * import ognl.Ognl; import ognl.OgnlException; try { result = Ognl.getValue(expression, root); }
 * catch (OgnlException ex) { // Report error or recover }
 * 
*

* This will parse the expression given and evaluate it against the root object given, returning the * result. If there is an error in the expression, such as the property is not found, the exception * is encapsulated into an {@link OgnlException OgnlException}. *

* Other more sophisticated uses of Ognl can pre-parse expressions. This provides two advantages: in * the case of user-supplied expressions it allows you to catch parse errors before evaluation and * it allows you to cache parsed expressions into an AST for better speed during repeated use. The * pre-parsed expression is always returned as an Object to simplify use for programs * that just wish to store the value for repeated use and do not care that it is an AST. If it does * care it can always safely cast the value to an AST type. *

* The Ognl class also takes a context map as one of the parameters to the set and get * methods. This allows you to put your own variables into the available namespace for OGNL * expressions. The default context contains only the #root and #context * keys, which are required to be present. The addDefaultContext(Object, Map) method * will alter an existing Map to put the defaults in. Here is an example that shows * how to extract the documentName property out of the root object and append a * string with the current user name in parens: *

 * private Map context = new HashMap(); public void setUserName(String value) {
 * context.put("userName", value); } try { // get value using our own custom context map result =
 * Ognl.getValue("documentName + \" (\" + ((#userName == null) ? \"<nobody>\" : #userName) +
 * \")\"", context, root); } catch (OgnlException ex) { // Report error or recover }
 * 
*/ public abstract class Ognl { private static volatile Integer expressionMaxLength = null; private static volatile Boolean expressionMaxLengthFrozen = Boolean.FALSE; /** * Applies a maximum allowed length on OGNL expressions for security reasons. * * @param expressionMaxLength the OGNL expressions maximum allowed length. Use null (default) to disable this functionality. * @throws SecurityException if the caller is inside OGNL expression itself. * @throws IllegalStateException if the expression maximum allowed length is frozen. * @throws IllegalArgumentException if the provided expressionMaxLength is < 0. * @since 3.1.26 */ public static synchronized void applyExpressionMaxLength(Integer expressionMaxLength) { if (expressionMaxLengthFrozen) { throw new IllegalStateException("The OGNL expression maximum allowed length has been frozen and cannot be changed."); } if (expressionMaxLength != null && expressionMaxLength < 0) { throw new IllegalArgumentException("The provided OGNL expression maximum allowed length, " + expressionMaxLength + ", is illegal."); } else { Ognl.expressionMaxLength = expressionMaxLength; } } /** * Freezes (prevents updates to) the maximum allowed length on OGNL expressions at the current value. * This makes it clear to other OGNL callers that the value should not be changed. * * @throws SecurityException if the caller is inside OGNL expression itself. * @since 3.1.26 */ public static synchronized void freezeExpressionMaxLength() { Ognl.expressionMaxLengthFrozen = Boolean.TRUE; } /** * Thaws (allows updates to) the maximum allowed length on OGNL expressions. * This makes it clear to other OGNL callers that the value can (again) be changed. * * @throws SecurityException if the caller is inside OGNL expression itself. * @since 3.1.26 */ public static synchronized void thawExpressionMaxLength() { Ognl.expressionMaxLengthFrozen = Boolean.FALSE; } /** * Parses the given OGNL expression and returns a tree representation of the expression that can * be used by Ognl static methods. * * @param expression the OGNL expression to be parsed * @return a tree representation of the expression * @throws ExpressionSyntaxException if the expression is malformed * @throws OgnlException if there is a pathological environmental problem */ public static Object parseExpression(String expression) throws OgnlException { final Integer currentExpressionMaxLength = Ognl.expressionMaxLength; // Limit access to the volatile variable to a single operation if (currentExpressionMaxLength != null && expression != null && expression.length() > currentExpressionMaxLength) { throw new OgnlException("Parsing blocked due to security reasons!", new SecurityException("This expression exceeded maximum allowed length: " + expression)); } try { assert expression != null; OgnlParser parser = new OgnlParser(new StringReader(expression)); return parser.topLevelExpression(); } catch (ParseException | TokenMgrError e) { throw new ExpressionSyntaxException(expression, e); } } /** * Parses and compiles the given expression using the {@link OgnlExpressionCompiler} returned * from {@link OgnlRuntime#getCompiler()}. * * @param context The context to use. * @param root The root object for the given expression. * @param expression The expression to compile. * @return The node with a compiled accessor set on {@link Node#getAccessor()} if compilation * was successfull. In instances where compilation wasn't possible because of a partially null * expression the {@link ExpressionAccessor} instance may be null and the compilation of this expression * still possible at some as yet indertermined point in the future. * @throws Exception If a compilation error occurs. */ public static > Node compileExpression(C context, Object root, String expression) throws Exception { Node expr = (Node) Ognl.parseExpression(expression); OgnlRuntime.compileExpression(context, expr, root); return expr; } /** * Creates and returns a new standard naming context for evaluating an OGNL expression. * * @param root the root of the object graph * @return a new {@link OgnlContext} with the keys root and context set * appropriately */ public static > C createDefaultContext(Object root) { MemberAccess memberAccess = new AbstractMemberAccess<>() { @Override public boolean isAccessible(C context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); return Modifier.isPublic(modifiers); } }; return addDefaultContext(root, memberAccess, null, null, null); } /** * Creates and returns a new standard naming context for evaluating an OGNL expression. * * @param root the root of the object graph * @return a new {@link OgnlContext} with the keys root and context set * appropriately */ public static > C createDefaultContext(Object root, Map values) { C context = createDefaultContext(root); context.setValues(values); return context; } /** * Creates and returns a new standard naming context for evaluating an OGNL expression. * * @param root The root of the object graph. * @param classResolver The resolver used to instantiate {@link Class} instances referenced in the expression. * @return a new OgnlContext with the keys root and context set * appropriately */ public static > C createDefaultContext(Object root, ClassResolver classResolver) { MemberAccess memberAccess = new AbstractMemberAccess<>() { @Override public boolean isAccessible(C context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); return Modifier.isPublic(modifiers); } }; return addDefaultContext(root, memberAccess, classResolver, null, null); } /** * Creates and returns a new standard naming context for evaluating an OGNL expression. * * @param root The root of the object graph. * @param classResolver The resolver used to instantiate {@link Class} instances referenced in the expression. * @param converter Converter used to convert return types of an expression in to their desired types. * @return a new {@link OgnlContext} with the keys root and context set * appropriately */ public static > C createDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter) { MemberAccess memberAccess = new AbstractMemberAccess<>() { @Override public boolean isAccessible(C context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); return Modifier.isPublic(modifiers); } }; return addDefaultContext(root, memberAccess, classResolver, converter, null); } /** * Creates and returns a new standard naming context for evaluating an OGNL expression. * * @param root The root of the object graph. * @param memberAccess Java security handling object to determine semantics for accessing normally private/protected * methods / fields. * @param classResolver The resolver used to instantiate {@link Class} instances referenced in the expression. * @param converter Converter used to convert return types of an expression in to their desired types. * @return a new {@link OgnlContext} with the keys root and context set * appropriately */ public static > C createDefaultContext(Object root, MemberAccess memberAccess, ClassResolver classResolver, TypeConverter converter) { return addDefaultContext(root, memberAccess, classResolver, converter, null); } /** * Creates and returns a new standard naming context for evaluating an OGNL expression. * * @param root The root of the object graph. * @param memberAccess Java security handling object to determine semantics for accessing normally private/protected * methods / fields. * @return a new {@link OgnlContext} with the keys root and context set * appropriately */ public static > C createDefaultContext(Object root, MemberAccess memberAccess) { return addDefaultContext(root, memberAccess, null, null, null); } /** * Appends the standard naming context for evaluating an OGNL expression into the context given * so that cached maps can be used as a context. * * @param root the root of the object graph * @param context the context to which OGNL context will be added. * @return {@link OgnlContext} with the keys root and context set * appropriately * @deprecated use ono of {{addDefaultContext()}} which accepts {@link MemberAccess} */ @Deprecated(forRemoval = true) public static > C addDefaultContext(Object root, C context) { MemberAccess memberAccess = new AbstractMemberAccess<>() { @Override public boolean isAccessible(C context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); return Modifier.isPublic(modifiers); } }; return addDefaultContext(root, memberAccess, null, null, context); } /** * Appends the standard naming context for evaluating an OGNL expression into the context given * so that cached maps can be used as a context. * * @param root The root of the object graph. * @param classResolver The resolver used to instantiate {@link Class} instances referenced in the expression. * @param context The context to which OGNL context will be added. * @return Context Map with the keys root and context set * appropriately * @deprecated use ono of {{addDefaultContext()}} which accepts {@link MemberAccess} */ @Deprecated(forRemoval = true) public static > C addDefaultContext(Object root, ClassResolver classResolver, C context) { MemberAccess memberAccess = new AbstractMemberAccess<>() { @Override public boolean isAccessible(C context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); return Modifier.isPublic(modifiers); } }; return addDefaultContext(root, memberAccess, classResolver, null, context); } /** * Appends the standard naming context for evaluating an OGNL expression into the context given * so that cached maps can be used as a context. * * @param root The root of the object graph. * @param classResolver The resolver used to instantiate {@link Class} instances referenced in the expression. * @param converter Converter used to convert return types of an expression in to their desired types. * @param context The context to which OGNL context will be added. * @return Context Map with the keys root and context set * appropriately * @deprecated use ono of {{addDefaultContext()}} which accepts {@link MemberAccess} */ @Deprecated(forRemoval = true) public static > C addDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, C context) { MemberAccess memberAccess = new AbstractMemberAccess<>() { @Override public boolean isAccessible(C context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); return Modifier.isPublic(modifiers); } }; return addDefaultContext(root, memberAccess, classResolver, converter, context); } public static > C addDefaultContext(Object root, MemberAccess memberAccess, ClassResolver classResolver, TypeConverter converter) { return addDefaultContext(root, memberAccess, classResolver, converter, null); } /** * Appends the standard naming context for evaluating an OGNL expression into the context given * so that cached maps can be used as a context. * * @param root the root of the object graph * @param memberAccess Definition for handling private/protected access. * @param classResolver The class loading resolver that should be used to resolve class references. * @param converter The type converter to be used by default. * @param initialContext Default context to use, if not an {@link OgnlContext} will be dumped into * a new {@link OgnlContext} object. * @return Context Map with the keys root and context set * appropriately */ public static > C addDefaultContext(Object root, MemberAccess memberAccess, ClassResolver classResolver, TypeConverter converter, Map initialContext) { return getBuilderProvider(memberAccess) .withMemberAccess(memberAccess) .withClassResolver(classResolver) .withTypeConverter(converter) .withInitialContext(initialContext) .withRoot(root) .build(); } private static final AtomicReference> builderProvider = new AtomicReference<>(); public static > OgnlContext.Builder getBuilderProvider(MemberAccess memberAccess) { @SuppressWarnings("unchecked") OgnlContext.Builder builder = (OgnlContext.Builder) Ognl.builderProvider.get(); return Objects.requireNonNullElseGet(builder, () -> new OgnlContext.Builder<>(b -> { OgnlContext context = new OgnlContext<>(memberAccess, b.getClassResolver(), b.getTypeConverter(), b.getInitialContext()); // Preserve the original root context when it exists and has user-defined variables, // but allow setting a new root in normal cases (e.g., initial context creation) Map initialContext = b.getInitialContext(); Object newRoot = b.getRoot(); // Check if initialContext is an OgnlContext to apply root preservation logic from PR #449 if (initialContext instanceof OgnlContext) { @SuppressWarnings("unchecked") OgnlContext ognlInitialContext = (OgnlContext) initialContext; if (ognlInitialContext.getRoot() != null && ognlInitialContext.size() > 0 && newRoot != ognlInitialContext.getRoot()) { // Only preserve the original root if the context has user variables and // the new root is different (indicating nested evaluation like list processing) return context.withRoot(ognlInitialContext.getRoot()); } } // Default behavior: set the new root return context.withRoot(newRoot); })); } public static > void withBuilderProvider(OgnlContext.Builder provider) { Ognl.builderProvider.set(provider); } /** * Gets the currently configured {@link TypeConverter} for the given context - if any. * * @param context The context to get the converter from. * @return The converter - or null if none found. */ public static > TypeConverter getTypeConverter(C context) { if (context != null) { return context.getTypeConverter(); } return null; } /** * Sets the root object to use for all expressions in the given context - doesn't necessarily replace * root object instances explicitly passed in to other expression resolving methods on this class. * * @param context The context to store the root object in. * @param root The root object. */ public static > void setRoot(C context, Object root) { context.withRoot(root); } /** * Gets the stored root object for the given context - if any. * * @param context The context to get the root object from. * @return The root object - or null if none found. */ public static > Object getRoot(C context) { return context.getRoot(); } /** * Gets the last {@link Evaluation} executed on the given context. * * @param context The context to get the evaluation from. * @return The {@link Evaluation} - or null if none was found. */ public static > Evaluation getLastEvaluation(C context) { return context.getLastEvaluation(); } /** * Evaluates the given OGNL expression tree to extract a value from the given root object. The * default context is set for the given context and root via addDefaultContext(). * * @param tree the OGNL expression tree to evaluate, as returned by parseExpression() * @param context the naming context for the evaluation * @param root the root object for the OGNL expression * @return the result of evaluating the expression * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ public static > Object getValue(Object tree, C context, Object root) throws OgnlException { return getValue(tree, context, root, null); } /** * Evaluates the given OGNL expression tree to extract a value from the given root object. The * default context is set for the given context and root via addDefaultContext(). * * @param tree the OGNL expression tree to evaluate, as returned by parseExpression() * @param context the naming context for the evaluation * @param root the root object for the OGNL expression * @param resultType the converted type of the resultant object, using the context's type converter * @return the result of evaluating the expression * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ public static > Object getValue(Object tree, C context, Object root, Class resultType) throws OgnlException { Object result; // Preserve original root context during nested evaluations (Issue #472) // Only update the context root if: // 1. Context has no root yet (initial evaluation), OR // 2. Root is the same as context root (not a nested call), OR // 3. Context is empty (no user variables) // This prevents nested evaluations (like lambda expressions in list operations) // from overwriting the original root, making #root references inaccessible boolean shouldUpdateRoot = context.getRoot() == null || context.getRoot() == root || context.size() == 0; C evaluationContext = shouldUpdateRoot ? context.withRoot(root) : context; Node node = (Node) tree; if (node.getAccessor() != null) { result = node.getAccessor().get(evaluationContext, root); } else { result = node.getValue(evaluationContext, root); } if (resultType != null) { result = getTypeConverter(evaluationContext).convertValue(evaluationContext, root, null, null, result, resultType); } return result; } /** * Gets the value represented by the given pre-compiled expression on the specified root * object. * * @param expression The pre-compiled expression, as found in {@link Node#getAccessor()}. * @param context The ognl context. * @param root The object to retrieve the expression value from. * @return The value. */ public static > Object getValue(ExpressionAccessor expression, C context, Object root) { return expression.get(context, root); } /** * Gets the value represented by the given pre-compiled expression on the specified root * object. * * @param expression The pre-compiled expression, as found in {@link Node#getAccessor()}. * @param context The ognl context. * @param root The object to retrieve the expression value from. * @param resultType The desired object type that the return value should be converted to using the {@link #getTypeConverter(OgnlContext)} }. * @return The value. */ public static > Object getValue(ExpressionAccessor expression, C context, Object root, Class resultType) { return getTypeConverter(context).convertValue(context, root, null, null, expression.get(context, root), resultType); } /** * Evaluates the given OGNL expression to extract a value from the given root object in a given * context * * @param expression the OGNL expression to be parsed * @param context the naming context for the evaluation * @param root the root object for the OGNL expression * @return the result of evaluating the expression * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem * @see #parseExpression(String) * @see #getValue(Object, Object) */ public static > Object getValue(String expression, C context, Object root) throws OgnlException { return getValue(expression, context, root, null); } /** * Evaluates the given OGNL expression to extract a value from the given root object in a given * context * * @param expression the OGNL expression to be parsed * @param context the naming context for the evaluation * @param root the root object for the OGNL expression * @param resultType the converted type of the resultant object, using the context's type converter * @return the result of evaluating the expression * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem * @see #parseExpression(String) * @see #getValue(Object, Object) */ public static > Object getValue(String expression, C context, Object root, Class resultType) throws OgnlException { return getValue(parseExpression(expression), context, root, resultType); } /** * Evaluates the given OGNL expression tree to extract a value from the given root object. * * @param tree the OGNL expression tree to evaluate, as returned by parseExpression() * @param root the root object for the OGNL expression * @return the result of evaluating the expression * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ public static Object getValue(Object tree, Object root) throws OgnlException { return getValue(tree, root, null); } /** * Evaluates the given OGNL expression tree to extract a value from the given root object. * * @param tree the OGNL expression tree to evaluate, as returned by parseExpression() * @param root the root object for the OGNL expression * @param resultType the converted type of the resultant object, using the context's type converter * @return the result of evaluating the expression * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ @SuppressWarnings("unchecked") public static > Object getValue(Object tree, Object root, Class resultType) throws OgnlException { return getValue(tree, (C) createDefaultContext(root), root, resultType); } /** * Convenience method that combines calls to parseExpression and * getValue. * * @param expression the OGNL expression to be parsed * @param root the root object for the OGNL expression * @return the result of evaluating the expression * @throws ExpressionSyntaxException if the expression is malformed * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem * @see #parseExpression(String) * @see #getValue(Object, Object) */ public static Object getValue(String expression, Object root) throws OgnlException { return getValue(expression, root, null); } /** * Convenience method that combines calls to parseExpression and * getValue. * * @param expression the OGNL expression to be parsed * @param root the root object for the OGNL expression * @param resultType the converted type of the resultant object, using the context's type converter * @return the result of evaluating the expression * @throws ExpressionSyntaxException if the expression is malformed * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem * @see #parseExpression(String) * @see #getValue(Object, Object) */ public static Object getValue(String expression, Object root, Class resultType) throws OgnlException { return getValue(parseExpression(expression), root, resultType); } /** * Evaluates the given OGNL expression tree to insert a value into the object graph rooted at * the given root object. The default context is set for the given context and root via addDefaultContext(). * * @param tree the OGNL expression tree to evaluate, as returned by parseExpression() * @param context the naming context for the evaluation * @param root the root object for the OGNL expression * @param value the value to insert into the object graph * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ @SuppressWarnings("unchecked") public static > void setValue(Object tree, C context, Object root, Object value) throws OgnlException { Node n = (Node) tree; if (n.getAccessor() != null) { n.getAccessor().set(context, root, value); return; } n.setValue(context, root, value); } /** * Sets the value given using the pre-compiled expression on the specified root * object. * * @param expression The pre-compiled expression, as found in {@link Node#getAccessor()}. * @param context The ognl context. * @param root The object to set the expression value on. * @param value The value to set. */ public static > void setValue(ExpressionAccessor expression, C context, Object root, Object value) { expression.set(context, root, value); } /** * Evaluates the given OGNL expression to insert a value into the object graph rooted at the * given root object given the context. * * @param expression the OGNL expression to be parsed * @param root the root object for the OGNL expression * @param context the naming context for the evaluation * @param value the value to insert into the object graph * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ public static > void setValue(String expression, C context, Object root, Object value) throws OgnlException { setValue(parseExpression(expression), context, root, value); } /** * Evaluates the given OGNL expression tree to insert a value into the object graph rooted at * the given root object. * * @param tree the OGNL expression tree to evaluate, as returned by parseExpression() * @param root the root object for the OGNL expression * @param value the value to insert into the object graph * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ @SuppressWarnings("unchecked") public static > void setValue(Object tree, Object root, Object value) throws OgnlException { setValue(tree, (C) createDefaultContext(root), root, value); } /** * Convenience method that combines calls to parseExpression and * setValue. * * @param expression the OGNL expression to be parsed * @param root the root object for the OGNL expression * @param value the value to insert into the object graph * @throws ExpressionSyntaxException if the expression is malformed * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem * @see #parseExpression(String) * @see #setValue(Object, Object, Object) */ public static void setValue(String expression, Object root, Object value) throws OgnlException { setValue(parseExpression(expression), root, value); } /** * Checks if the specified {@link Node} instance represents a constant * expression. * * @param tree The {@link Node} to check. * @param context The context to use. * @return True if the node is a constant - false otherwise. * @throws OgnlException If an error occurs checking the expression. */ public static > boolean isConstant(Object tree, C context) throws OgnlException { return ((SimpleNode) tree).isConstant(addDefaultContext(null, context)); } /** * Checks if the specified expression represents a constant expression. * * @param expression The expression to check. * @param context The context to use. * @return True if the node is a constant - false otherwise. * @throws OgnlException If an error occurs checking the expression. */ public static > boolean isConstant(String expression, C context) throws OgnlException { return isConstant(parseExpression(expression), context); } /** * Same as {@link #isConstant(String, OgnlContext)} - only the {@link Map} context * is created for you. * * @param tree The {@link Node} to check. * @return True if the node represents a constant expression - false otherwise. * @throws OgnlException If an exception occurs. */ @SuppressWarnings("unchecked") public static > boolean isConstant(Object tree) throws OgnlException { return isConstant(tree, (C) createDefaultContext(null)); } /** * Same as {@link #isConstant(String, OgnlContext)} - only the {@link Map} * instance is created for you. * * @param expression The expression to check. * @return True if the expression represents a constant - false otherwise. * @throws OgnlException If an exception occurs. */ @SuppressWarnings("unchecked") public static > boolean isConstant(String expression) throws OgnlException { return isConstant(parseExpression(expression), (C) createDefaultContext(null)); } public static > boolean isSimpleProperty(Object tree, C context) throws OgnlException { return ((SimpleNode) tree).isSimpleProperty(addDefaultContext(null, context)); } public static > boolean isSimpleProperty(String expression, C context) throws OgnlException { return isSimpleProperty(parseExpression(expression), context); } public static > boolean isSimpleProperty(Object tree) throws OgnlException { return isSimpleProperty(tree, (C) createDefaultContext(null)); } public static > boolean isSimpleProperty(String expression) throws OgnlException { return isSimpleProperty(parseExpression(expression), (C) createDefaultContext(null)); } public static > boolean isSimpleNavigationChain(Object tree, C context) throws OgnlException { return ((SimpleNode) tree).isSimpleNavigationChain(context); } public static > boolean isSimpleNavigationChain(String expression, C context) throws OgnlException { return isSimpleNavigationChain(parseExpression(expression), context); } public static > boolean isSimpleNavigationChain(Object tree) throws OgnlException { return isSimpleNavigationChain(tree, (C) createDefaultContext(null)); } public static > boolean isSimpleNavigationChain(String expression) throws OgnlException { return isSimpleNavigationChain(parseExpression(expression), (C) createDefaultContext(null)); } /** * You can't make one of these. */ private Ognl() { } } ================================================ FILE: ognl/src/main/java/ognl/OgnlCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.internal.Cache; import ognl.internal.CacheException; import ognl.internal.CacheFactory; import ognl.internal.ClassCache; import ognl.internal.ClassCacheHandler; import ognl.internal.HashMapCacheFactory; import ognl.internal.entry.DeclaredMethodCacheEntry; import ognl.internal.entry.DeclaredMethodCacheEntryFactory; import ognl.internal.entry.FieldCacheEntryFactory; import ognl.internal.entry.GenericMethodParameterTypeCacheEntry; import ognl.internal.entry.GenericMethodParameterTypeFactory; import ognl.internal.entry.MethodAccessCacheEntryFactory; import ognl.internal.entry.MethodAccessEntryValue; import ognl.internal.entry.PropertyDescriptorCacheEntryFactory; import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This class takes care of all the internal caching for OGNL. */ public class OgnlCache { private final CacheFactory cacheFactory = new HashMapCacheFactory(); private final ClassCache methodAccessors = cacheFactory.createClassCache(); { MethodAccessor methodAccessor = new ObjectMethodAccessor(); setMethodAccessor(Object.class, methodAccessor); setMethodAccessor(byte[].class, methodAccessor); setMethodAccessor(short[].class, methodAccessor); setMethodAccessor(char[].class, methodAccessor); setMethodAccessor(int[].class, methodAccessor); setMethodAccessor(long[].class, methodAccessor); setMethodAccessor(float[].class, methodAccessor); setMethodAccessor(double[].class, methodAccessor); setMethodAccessor(Object[].class, methodAccessor); } private final ClassCache propertyAccessors = cacheFactory.createClassCache(); { PropertyAccessor propertyAccessor = new ArrayPropertyAccessor(); setPropertyAccessor(Object.class, new ObjectPropertyAccessor()); setPropertyAccessor(byte[].class, propertyAccessor); setPropertyAccessor(short[].class, propertyAccessor); setPropertyAccessor(char[].class, propertyAccessor); setPropertyAccessor(int[].class, propertyAccessor); setPropertyAccessor(long[].class, propertyAccessor); setPropertyAccessor(float[].class, propertyAccessor); setPropertyAccessor(double[].class, propertyAccessor); setPropertyAccessor(Object[].class, propertyAccessor); setPropertyAccessor(List.class, new ListPropertyAccessor()); setPropertyAccessor(Map.class, new MapPropertyAccessor()); setPropertyAccessor(Set.class, new SetPropertyAccessor()); setPropertyAccessor(Iterator.class, new IteratorPropertyAccessor()); setPropertyAccessor(Enumeration.class, new EnumerationPropertyAccessor()); } private final ClassCache elementsAccessors = cacheFactory.createClassCache(); { ElementsAccessor elementsAccessor = new ArrayElementsAccessor(); setElementsAccessor(Object.class, new ObjectElementsAccessor()); setElementsAccessor(byte[].class, elementsAccessor); setElementsAccessor(short[].class, elementsAccessor); setElementsAccessor(char[].class, elementsAccessor); setElementsAccessor(int[].class, elementsAccessor); setElementsAccessor(long[].class, elementsAccessor); setElementsAccessor(float[].class, elementsAccessor); setElementsAccessor(double[].class, elementsAccessor); setElementsAccessor(Object[].class, elementsAccessor); setElementsAccessor(Collection.class, new CollectionElementsAccessor()); setElementsAccessor(Map.class, new MapElementsAccessor()); setElementsAccessor(Iterator.class, new IteratorElementsAccessor()); setElementsAccessor(Enumeration.class, new EnumerationElementsAccessor()); setElementsAccessor(Number.class, new NumberElementsAccessor()); } private final ClassCache nullHandlers = cacheFactory.createClassCache(); { NullHandler nullHandler = new ObjectNullHandler(); setNullHandler(Object.class, nullHandler); setNullHandler(byte[].class, nullHandler); setNullHandler(short[].class, nullHandler); setNullHandler(char[].class, nullHandler); setNullHandler(int[].class, nullHandler); setNullHandler(long[].class, nullHandler); setNullHandler(float[].class, nullHandler); setNullHandler(double[].class, nullHandler); setNullHandler(Object[].class, nullHandler); } final ClassCache> propertyDescriptorCache = cacheFactory.createClassCache(new PropertyDescriptorCacheEntryFactory()); private final ClassCache>> constructorCache = cacheFactory.createClassCache(key -> Arrays.asList(key.getConstructors())); private final Cache>> methodCache = cacheFactory.createCache(new DeclaredMethodCacheEntryFactory()); private final ClassCache> fieldCache = cacheFactory.createClassCache(new FieldCacheEntryFactory()); private final Cache[]> methodParameterTypesCache = cacheFactory.createCache(Method::getParameterTypes); final Cache[]> genericMethodParameterTypesCache = cacheFactory.createCache(new GenericMethodParameterTypeFactory()); private final Cache, Class[]> ctorParameterTypesCache = cacheFactory.createCache(Constructor::getParameterTypes); private final Cache methodAccessCache = cacheFactory.createCache(new MethodAccessCacheEntryFactory()); public Class[] getMethodParameterTypes(Method method) throws CacheException { return methodParameterTypesCache.get(method); } public Class[] getParameterTypes(Constructor constructor) throws CacheException { return ctorParameterTypesCache.get(constructor); } public List> getConstructor(Class clazz) throws CacheException { return constructorCache.get(clazz); } public Map getField(Class clazz) throws CacheException { return fieldCache.get(clazz); } public Map> getMethod(DeclaredMethodCacheEntry declaredMethodCacheEntry) throws CacheException { return methodCache.get(declaredMethodCacheEntry); } public Map getPropertyDescriptor(Class clazz) throws CacheException { return propertyDescriptorCache.get(clazz); } public > MethodAccessor getMethodAccessor(Class clazz) throws OgnlException { MethodAccessor methodAccessor = ClassCacheHandler.getHandler(clazz, methodAccessors); if (methodAccessor != null) { return methodAccessor; } throw new OgnlException("No method accessor for " + clazz); } public void setMethodAccessor(Class clazz, MethodAccessor accessor) { methodAccessors.put(clazz, accessor); } public void setPropertyAccessor(Class clazz, PropertyAccessor accessor) { propertyAccessors.put(clazz, accessor); } public > PropertyAccessor getPropertyAccessor(Class clazz) throws OgnlException { PropertyAccessor propertyAccessor = ClassCacheHandler.getHandler(clazz, propertyAccessors); if (propertyAccessor != null) { return propertyAccessor; } throw new OgnlException("No property accessor for class " + clazz); } /** * Registers the specified {@link ClassCacheInspector} with all class reflection based internal caches. This may * have a significant performance impact so be careful using this in production scenarios. * * @param inspector The inspector instance that will be registered with all internal cache instances. */ public void setClassCacheInspector(ClassCacheInspector inspector) { propertyDescriptorCache.setClassInspector(inspector); constructorCache.setClassInspector(inspector); fieldCache.setClassInspector(inspector); } public Class[] getGenericMethodParameterTypes(GenericMethodParameterTypeCacheEntry key) throws CacheException { return genericMethodParameterTypesCache.get(key); } @Deprecated(since = "3.4.6", forRemoval = true) public boolean getMethodPerm(Method method) throws CacheException { return true; } public MethodAccessEntryValue getMethodAccess(Method method) throws CacheException { return methodAccessCache.get(method); } public void clear() { methodParameterTypesCache.clear(); ctorParameterTypesCache.clear(); propertyDescriptorCache.clear(); genericMethodParameterTypesCache.clear(); constructorCache.clear(); methodCache.clear(); fieldCache.clear(); methodAccessCache.clear(); } public ElementsAccessor getElementsAccessor(Class clazz) throws OgnlException { ElementsAccessor answer = ClassCacheHandler.getHandler(clazz, elementsAccessors); if (answer != null) { return answer; } throw new OgnlException("No elements accessor for class " + clazz); } public void setElementsAccessor(Class clazz, ElementsAccessor accessor) { elementsAccessors.put(clazz, accessor); } public > NullHandler getNullHandler(Class clazz) throws OgnlException { NullHandler answer = ClassCacheHandler.getHandler(clazz, nullHandlers); if (answer != null) { return answer; } throw new OgnlException("No null handler for class " + clazz); } public void setNullHandler(Class clazz, NullHandler handler) { nullHandlers.put(clazz, handler); } } ================================================ FILE: ognl/src/main/java/ognl/OgnlContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.LocalReference; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; /** * This class defines the execution context for an OGNL expression */ public class OgnlContext> implements Map { private static final String ROOT_CONTEXT_KEY = "root"; private static final String THIS_CONTEXT_KEY = "this"; private static final String TRACE_EVALUATIONS_CONTEXT_KEY = "_traceEvaluations"; private static final String LAST_EVALUATION_CONTEXT_KEY = "_lastEvaluation"; private static final String KEEP_LAST_EVALUATION_CONTEXT_KEY = "_keepLastEvaluation"; private static final String IGNORE_READ_METHODS_CONTEXT_KEY = "_ignoreReadMethods"; private static final String PROPERTY_KEY_PREFIX = "ognl"; private static final boolean DEFAULT_IGNORE_READ_METHODS = false; public static final String CURRENT_CHAIN = "_currentChain"; public static final String LAST_CHILD = "_lastChild"; private static boolean DEFAULT_TRACE_EVALUATIONS = false; private static boolean DEFAULT_KEEP_LAST_EVALUATION = false; private static final Map RESERVED_KEYS = new HashMap<>(6); private Object root; private Object currentObject; private Node currentNode; private boolean traceEvaluations = DEFAULT_TRACE_EVALUATIONS; private Evaluation rootEvaluation; private Evaluation currentEvaluation; private Evaluation lastEvaluation; private boolean keepLastEvaluation = DEFAULT_KEEP_LAST_EVALUATION; private boolean ignoreReadMethods = DEFAULT_IGNORE_READ_METHODS; protected final Map internalContext; private final MemberAccess memberAccess; private final ClassResolver classResolver; private final TypeConverter typeConverter; static { RESERVED_KEYS.put(ROOT_CONTEXT_KEY, null); RESERVED_KEYS.put(THIS_CONTEXT_KEY, null); RESERVED_KEYS.put(TRACE_EVALUATIONS_CONTEXT_KEY, null); RESERVED_KEYS.put(LAST_EVALUATION_CONTEXT_KEY, null); RESERVED_KEYS.put(KEEP_LAST_EVALUATION_CONTEXT_KEY, null); RESERVED_KEYS.put(IGNORE_READ_METHODS_CONTEXT_KEY, null); try { String property; if ((property = System.getProperty(PROPERTY_KEY_PREFIX + ".traceEvaluations")) != null) { DEFAULT_TRACE_EVALUATIONS = Boolean.parseBoolean(property.trim()); } if ((property = System.getProperty(PROPERTY_KEY_PREFIX + ".keepLastEvaluation")) != null) { DEFAULT_KEEP_LAST_EVALUATION = Boolean.parseBoolean(property.trim()); } } catch (SecurityException ex) { // restricted access environment, just keep defaults } } private final List> typeStack = new ArrayList<>(3); // size 3 should be enough stack for most expressions private final List> accessorStack = new ArrayList<>(3); // size 3 should be enough stack for most expressions private int localReferenceCounter = 0; private Map localReferenceMap = null; /** * Constructs a new OgnlContext with the given class resolver, type converter and member access. * If any of these parameters is null the default will be used, except memberAccess which must be non-null. * * @param classResolver the ClassResolver for a new OgnlContext. * @param typeConverter the TypeConverter for a new OgnlContext. * @param memberAccess the MemberAccess for a new OgnlContext. Must be non-null. * @deprecated use {@link OgnlContext(MemberAccess, ClassResolver, TypeConverter)} instead */ @Deprecated(forRemoval = true) public OgnlContext(ClassResolver classResolver, TypeConverter typeConverter, MemberAccess memberAccess) { this(memberAccess, classResolver, typeConverter, null); } public OgnlContext(MemberAccess memberAccess, ClassResolver classResolver, TypeConverter typeConverter) { this(memberAccess, classResolver, typeConverter, null); } public OgnlContext(MemberAccess memberAccess, ClassResolver classResolver) { this(memberAccess, classResolver, null, null); } public OgnlContext(MemberAccess memberAccess) { this(memberAccess, null, null, null); } /** * Constructs a new OgnlContext with the given member access, class resolver, type converter and values. * If any of these parameters is null the default will be used, except memberAccess which must be non-null. * * @param memberAccess the MemberAccess for a new OgnlContext. Must be non-null. * @param classResolver the ClassResolver for a new OgnlContext. * @param typeConverter the TypeConverter for a new OgnlContext. * @param initialContext the initial context of values to provide for a new OgnlContext. */ public OgnlContext(MemberAccess memberAccess, ClassResolver classResolver, TypeConverter typeConverter, Map initialContext) { if (memberAccess != null) { this.memberAccess = memberAccess; } else { throw new IllegalArgumentException("MemberAccess implementation must be provided - null not permitted!"); } this.classResolver = Objects.requireNonNullElseGet(classResolver, DefaultClassResolver::new); this.typeConverter = Objects.requireNonNullElseGet(typeConverter, DefaultTypeConverter::new); if (initialContext == null) { this.internalContext = new HashMap<>(23); } else { this.internalContext = new HashMap<>(initialContext); } } /** * Set (put) the provided value map content into the existing values Map for this OgnlContext. * * @param values a Map of additional values to put into this OgnlContext. */ public void setValues(Map values) { for (String k : values.keySet()) { put(k, values.get(k)); } } /** * Similar to {@link #setValues(Map)} but returns the current instance of {@link OgnlContext} * @param values a Map of values * @return the current instance of {@link OgnlContext} */ @SuppressWarnings("unchecked") public C withValues(Map values) { this.setValues(values); return (C) this; } /** * Get the values Map for this OgnlContext. * * @return Map of values for this OgnlContext. */ public Map getValues() { return internalContext; } public ClassResolver getClassResolver() { return classResolver; } public TypeConverter getTypeConverter() { return typeConverter; } public MemberAccess getMemberAccess() { return memberAccess; } /** * @deprecated use {@link #withRoot(Object)} instead */ @Deprecated(forRemoval = true) public void setRoot(Object value) { this.withRoot(value); } public Object getRoot() { return root; } /** * @deprecated since OGNL 3.4.0, use {@link #isTraceEvaluations()} */ @Deprecated public boolean getTraceEvaluations() { return isTraceEvaluations(); } public boolean isTraceEvaluations() { return traceEvaluations; } public void setTraceEvaluations(boolean value) { traceEvaluations = value; } public Evaluation getLastEvaluation() { return lastEvaluation; } public void setLastEvaluation(Evaluation value) { lastEvaluation = value; } /** * @deprecated since OGNL 3.4.0, use {@link #isKeepLastEvaluation()} */ @Deprecated public boolean getKeepLastEvaluation() { return isKeepLastEvaluation(); } /** * Returns true if the last evaluation that was done on this context is retained and available * through getLastEvaluation(). The default is true. * * @return true if the last evaluation for this context is retained and available through getLastEvaluation(), false otherwise. */ public boolean isKeepLastEvaluation() { return keepLastEvaluation; } /** * Sets whether the last evaluation that was done on this context is retained and available * through getLastEvaluation(). The default is true. * * @param value true if the last evaluation for this context should be retained and available through getLastEvaluation(), false otherwise. */ public void setKeepLastEvaluation(boolean value) { keepLastEvaluation = value; } /** * Returns true if read methods of properties are ignored when accessing properties. The default is false. * * @return true if read methods of properties are ignored when accessing properties, false otherwise. */ public boolean isIgnoreReadMethods() { return ignoreReadMethods; } /** * Sets read methods of properties are ignored when accessing properties. The default is false. * * @param value true if read methods of properties are ignored when accessing properties, false otherwise. */ public void setIgnoreReadMethods(boolean value) { this.ignoreReadMethods = value; } public void setCurrentObject(Object value) { currentObject = value; } public Object getCurrentObject() { return currentObject; } public void setCurrentAccessor(Class type) { accessorStack.add(type); } public Class getCurrentAccessor() { if (accessorStack.isEmpty()) return null; return accessorStack.get(accessorStack.size() - 1); } public Class getPreviousAccessor() { if (accessorStack.isEmpty()) return null; if (accessorStack.size() > 1) return accessorStack.get(accessorStack.size() - 2); else return null; } public Class getFirstAccessor() { if (accessorStack.isEmpty()) return null; return accessorStack.get(0); } /** * Gets the current class type being evaluated on the stack, as set by {@link #setCurrentType(Class)}. * * @return The current object type, may be null. */ public Class getCurrentType() { if (typeStack.isEmpty()) return null; return typeStack.get(typeStack.size() - 1); } public void setCurrentType(Class type) { typeStack.add(type); } /** * Represents the last known object type on the evaluation stack, will be the value of * the last known {@link #getCurrentType()}. * * @return The previous type of object on the stack, may be null. */ public Class getPreviousType() { if (typeStack.isEmpty()) return null; if (typeStack.size() > 1) return typeStack.get(typeStack.size() - 2); else return null; } public void setPreviousType(Class type) { if (typeStack.isEmpty() || typeStack.size() < 2) return; typeStack.set(typeStack.size() - 2, type); } public Class getFirstType() { if (typeStack.isEmpty()) return null; return typeStack.get(0); } public void setCurrentNode(Node value) { currentNode = value; } public Node getCurrentNode() { return currentNode; } /** * Gets the current Evaluation from the top of the stack. This is the Evaluation that is in * process of evaluating. * * @return the current Evaluation from the top of the stack (being evaluated). */ public Evaluation getCurrentEvaluation() { return currentEvaluation; } public void setCurrentEvaluation(Evaluation value) { currentEvaluation = value; } /** * Gets the root of the evaluation stack. This Evaluation contains the node representing the * root expression and the source is the root source object. * * @return the root Evaluation from the stack (the root expression node). */ public Evaluation getRootEvaluation() { return rootEvaluation; } public void setRootEvaluation(Evaluation value) { rootEvaluation = value; } /** * Returns the Evaluation at the relative index given. This should be zero or a negative number * as a relative reference back up the evaluation stack. Therefore getEvaluation(0) returns the * current Evaluation. * * @param relativeIndex the relative index for the Evaluation to retrieve from the stack (with 0 being the current Evaluation). relativeIndex should be <= 0. * @return the Evaluation at relativeIndex, or null if relativeIndex is > 0. */ public Evaluation getEvaluation(int relativeIndex) { Evaluation result = null; if (relativeIndex <= 0) { result = currentEvaluation; while ((++relativeIndex < 0) && (result != null)) { result = result.getParent(); } } return result; } /** * Pushes a new Evaluation onto the stack. This is done before a node evaluates. When evaluation * is complete it should be popped from the stack via popEvaluation(). * * @param value the Evaluation to push onto the stack. */ public void pushEvaluation(Evaluation value) { if (currentEvaluation != null) { currentEvaluation.addChild(value); } else { setRootEvaluation(value); } setCurrentEvaluation(value); } /** * Pops the current Evaluation off of the top of the stack. This is done after a node has * completed its evaluation. * * @return the Evaluation popped from the top of the stack. */ public Evaluation popEvaluation() { Evaluation result; result = currentEvaluation; setCurrentEvaluation(result.getParent()); if (currentEvaluation == null) { setLastEvaluation(isKeepLastEvaluation() ? result : null); setRootEvaluation(null); setCurrentNode(null); } return result; } public int incrementLocalReferenceCounter() { return ++localReferenceCounter; } public void addLocalReference(String key, LocalReference reference) { if (localReferenceMap == null) { localReferenceMap = new LinkedHashMap<>(); } localReferenceMap.put(key, reference); } public Map getLocalReferences() { return localReferenceMap; } /* ================= Map interface ================= */ @Override public int size() { return internalContext.size(); } @Override public boolean isEmpty() { return internalContext.isEmpty(); } @Override public boolean containsKey(Object key) { return internalContext.containsKey(key); } @Override public boolean containsValue(Object value) { return internalContext.containsValue(value); } @Override public Object get(Object key) { if (key == null) { return null; } Object result; String strKey = key.toString(); if (RESERVED_KEYS.containsKey(strKey)) { switch (strKey) { case OgnlContext.THIS_CONTEXT_KEY: result = getCurrentObject(); break; case OgnlContext.ROOT_CONTEXT_KEY: result = getRoot(); break; case OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY: result = isTraceEvaluations() ? Boolean.TRUE : Boolean.FALSE; break; case OgnlContext.LAST_EVALUATION_CONTEXT_KEY: result = getLastEvaluation(); break; case OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY: result = isKeepLastEvaluation() ? Boolean.TRUE : Boolean.FALSE; break; case OgnlContext.IGNORE_READ_METHODS_CONTEXT_KEY: result = isIgnoreReadMethods() ? Boolean.TRUE : Boolean.FALSE; break; default: throw new IllegalArgumentException("unknown reserved key '" + key + "'"); } } else { result = internalContext.get(key); } return result; } @Override public Object put(String key, Object value) { Object result; if (RESERVED_KEYS.containsKey(key)) { switch (key) { case OgnlContext.THIS_CONTEXT_KEY: result = getCurrentObject(); setCurrentObject(value); break; case OgnlContext.ROOT_CONTEXT_KEY: result = getRoot(); setRoot(value); break; case OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY: result = isTraceEvaluations() ? Boolean.TRUE : Boolean.FALSE; setTraceEvaluations(OgnlOps.booleanValue(value)); break; case OgnlContext.LAST_EVALUATION_CONTEXT_KEY: result = getLastEvaluation(); lastEvaluation = (Evaluation) value; break; case OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY: result = isKeepLastEvaluation() ? Boolean.TRUE : Boolean.FALSE; setKeepLastEvaluation(OgnlOps.booleanValue(value)); break; case OgnlContext.IGNORE_READ_METHODS_CONTEXT_KEY: result = isIgnoreReadMethods() ? Boolean.TRUE : Boolean.FALSE; setIgnoreReadMethods(OgnlOps.booleanValue(value)); break; default: throw new IllegalArgumentException("unknown reserved key '" + key + "'"); } } else { result = internalContext.put(key, value); } return result; } @Override public Object remove(Object key) { Object result; if (key == null) { return internalContext.remove(key); } String strKey = key.toString(); if (RESERVED_KEYS.containsKey(strKey)) { switch (strKey) { case OgnlContext.THIS_CONTEXT_KEY: result = getCurrentObject(); setCurrentObject(null); break; case OgnlContext.ROOT_CONTEXT_KEY: result = getRoot(); setRoot(null); break; case OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY: throw new IllegalArgumentException("Can't remove " + OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY + " from context"); case OgnlContext.LAST_EVALUATION_CONTEXT_KEY: result = lastEvaluation; setLastEvaluation(null); break; case OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY: throw new IllegalArgumentException("Can't remove " + OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY + " from context"); case OgnlContext.IGNORE_READ_METHODS_CONTEXT_KEY: throw new IllegalArgumentException("Can't remove " + OgnlContext.IGNORE_READ_METHODS_CONTEXT_KEY + " from context"); default: throw new IllegalArgumentException("Unknown reserved key '" + key + "'"); } } else { result = internalContext.remove(key); } return result; } @Override public void putAll(Map t) { for (Map.Entry entry : t.entrySet()) { put(entry.getKey(), entry.getValue()); } } @Override public void clear() { internalContext.clear(); typeStack.clear(); accessorStack.clear(); localReferenceCounter = 0; if (localReferenceMap != null) { localReferenceMap.clear(); } setRoot(null); setCurrentObject(null); setRootEvaluation(null); setCurrentEvaluation(null); setLastEvaluation(null); setCurrentNode(null); } @Override public Set keySet() { return Collections.unmodifiableSet(internalContext.keySet()); } public Collection values() { return Collections.unmodifiableCollection(internalContext.values()); } @Override public Set> entrySet() { return Collections.unmodifiableSet(internalContext.entrySet()); } @Override public boolean equals(Object other) { if (!(other instanceof OgnlContext)) { return false; } @SuppressWarnings("rawtypes") OgnlContext otherContext = (OgnlContext) other; return internalContext.equals(otherContext.internalContext); } @Override public int hashCode() { return internalContext.hashCode(); } public C withRoot(Object value) { root = value; accessorStack.clear(); typeStack.clear(); currentObject = value; if (currentObject != null) { setCurrentType(currentObject.getClass()); } return (C) this; } public static class Builder> { private final Function, C> provider; private Object root; private MemberAccess memberAccess; private ClassResolver classResolver; private TypeConverter typeConverter; private Map initialContext; public Builder(Function, C> provider) { this.provider = provider; } public Builder withMemberAccess(MemberAccess memberAccess) { if (memberAccess == null) { throw new IllegalArgumentException("MemberAccess is required"); } this.memberAccess = memberAccess; return this; } public Builder withClassResolver(ClassResolver classResolver) { this.classResolver = classResolver; return this; } public Builder withTypeConverter(TypeConverter converter) { this.typeConverter = converter; return this; } public Builder withRoot(Object value) { root = value; return this; } public Builder withInitialContext(Map initialContext) { this.initialContext = initialContext; return this; } public MemberAccess getMemberAccess() { return memberAccess; } public ClassResolver getClassResolver() { return classResolver; } public TypeConverter getTypeConverter() { return typeConverter; } public Map getInitialContext() { return initialContext; } public Object getRoot() { return root; } public C build() { return provider.apply(this); } } } ================================================ FILE: ognl/src/main/java/ognl/OgnlException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * Superclass for OGNL exceptions, incorporating an optional encapsulated exception. */ public class OgnlException extends Exception { private static final long serialVersionUID = 1225801032966287635L; /** * The root evaluation of the expression when the exception was thrown */ private Evaluation _evaluation; /** * Constructs an OgnlException with no message or encapsulated exception. */ public OgnlException() { this(null, null); } /** * Constructs an OgnlException with the given message but no encapsulated exception. * * @param msg the exception's detail message */ public OgnlException(String msg) { this(msg, null); } /** * Constructs an OgnlException with the given message and encapsulated exception. * * @param msg the exception's detail message * @param reason the encapsulated exception */ public OgnlException(String msg, Throwable reason) { super(msg, reason, true, false); } /** * Constructs an OgnlException with the given message and encapsulated exception, * with control on exception suppression and stack trace collection. * * @param message the exception's detail message * @param reason the encapsulated exception * @param enableSuppression whether suppression is enabled or disabled * @param writableStackTrace whether the stack trace should be writable * See {@link java.lang.Throwable#Throwable(String, Throwable, boolean, boolean)} for more info. */ protected OgnlException(String message, Throwable reason, boolean enableSuppression, boolean writableStackTrace) { super(message, reason, enableSuppression, writableStackTrace); } /** * Returns the encapsulated exception, or null if there is none. * * @return the encapsulated exception */ public Throwable getReason() { return getCause(); } /** * Returns the Evaluation that was the root evaluation when the exception was * thrown. * * @return The {@link Evaluation}. */ public Evaluation getEvaluation() { return _evaluation; } /** * Sets the Evaluation that was current when this exception was thrown. * * @param value The {@link Evaluation}. */ public void setEvaluation(Evaluation value) { _evaluation = value; } /** * Returns a string representation of this exception. * * @return a string representation of this exception */ public String toString() { if (getCause() == null) { return super.toString(); } return super.toString() + " [" + getCause() + "]"; } } ================================================ FILE: ognl/src/main/java/ognl/OgnlOps.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.UnsupportedCompilationException; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.util.Collection; import java.util.Enumeration; /** * This is an abstract class with static methods that define the operations of OGNL. */ public abstract class OgnlOps implements NumericTypes { /** * Compares two objects for equality, even if it has to convert one of them to the other type. * If both objects are numeric they are converted to the widest type and compared. If one is * non-numeric and one is numeric the non-numeric is converted to double and compared to the * double numeric value. If both are non-numeric and Comparable and the types are compatible * (i.e. v1 is of the same or superclass of v2's type) they are compared with * Comparable.compareTo(). If both values are non-numeric and not Comparable or of incompatible * classes this will throw and IllegalArgumentException. * * @param v1 First value to compare * @param v2 second value to compare * @return integer describing the comparison between the two objects. A negative number * indicates that v1 < v2. Positive indicates that v1 > v2. Zero indicates v1 == v2. * @throws IllegalArgumentException if the objects are both non-numeric yet of incompatible types or do not implement * Comparable. */ public static int compareWithConversion(Object v1, Object v2) { int result; if (v1 == v2) { result = 0; } else { int t1 = getNumericType(v1), t2 = getNumericType(v2), type = getNumericType(t1, t2, true); switch (type) { case BIGINT: result = bigIntValue(v1).compareTo(bigIntValue(v2)); break; case BIGDEC: result = bigDecValue(v1).compareTo(bigDecValue(v2)); break; case NONNUMERIC: if ((t1 == NONNUMERIC) && (t2 == NONNUMERIC)) { if ((v1 instanceof Comparable) && v1.getClass().isAssignableFrom(v2.getClass())) { result = ((Comparable) v1).compareTo(v2); break; } else if ((v1 instanceof Enum && v2 instanceof Enum) && (v1.getClass() == v2.getClass() || ((Enum) v1).getDeclaringClass() == ((Enum) v2).getDeclaringClass())) { result = ((Enum) v1).compareTo((Enum) v2); break; } else { throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and " + v2.getClass().getName()); } } // else fall through case FLOAT: case DOUBLE: double dv1 = doubleValue(v1), dv2 = doubleValue(v2); return (dv1 == dv2) ? 0 : ((dv1 < dv2) ? -1 : 1); default: long lv1 = longValue(v1), lv2 = longValue(v2); return (lv1 == lv2) ? 0 : ((lv1 < lv2) ? -1 : 1); } } return result; } /** * Returns true if object1 is equal to object2 in either the sense that they are the same object * or, if both are non-null if they are equal in the equals() sense. * * @param object1 First object to compare * @param object2 Second object to compare * @return true if v1 == v2 */ public static boolean isEqual(Object object1, Object object2) { boolean result = false; if (object1 == object2) { result = true; } else if (object1 != null && object2 != null) { if (object1.getClass().isArray()) { if (object2.getClass().isArray() && (object2.getClass() == object1.getClass())) { result = (Array.getLength(object1) == Array.getLength(object2)); if (result) { for (int i = 0, icount = Array.getLength(object1); result && (i < icount); i++) { result = isEqual(Array.get(object1, i), Array.get(object2, i)); } } } } else { int t1 = getNumericType(object1); int t2 = getNumericType(object2); // compare non-comparable non-numeric types by equals only if (t1 == NONNUMERIC && t2 == NONNUMERIC && (!(object1 instanceof Comparable) || !(object2 instanceof Comparable))) { result = object1.equals(object2); } else { result = compareWithConversion(object1, object2) == 0; } } } return result; } public static boolean booleanValue(boolean value) { return value; } public static boolean booleanValue(int value) { return value > 0; } public static boolean booleanValue(float value) { return value > 0; } public static boolean booleanValue(long value) { return value > 0; } public static boolean booleanValue(double value) { return value > 0; } /** * Evaluates the given object as a boolean: if it is a Boolean object, it's easy; if it's a * Number or a Character, returns true for non-zero objects; and otherwise returns true for * non-null objects. * * @param value an object to interpret as a boolean * @return the boolean value implied by the given object */ public static boolean booleanValue(Object value) { if (value == null) { return false; } Class c = value.getClass(); if (c == Boolean.class) { return (Boolean) value; } if (c == String.class) { return Boolean.parseBoolean(String.valueOf(value)); } if (c == Character.class) { return (Character) value != 0; } if (value instanceof Number) { return ((Number) value).doubleValue() != 0; } return true; // non-null } /** * Evaluates the given object as a long integer. * * @param value an object to interpret as a long integer * @return the long integer value implied by the given object * @throws NumberFormatException if the given object can't be understood as a long integer */ public static long longValue(Object value) throws NumberFormatException { if (value == null) return 0L; Class c = value.getClass(); if (c.getSuperclass() == Number.class) return ((Number) value).longValue(); if (c == Boolean.class) return (Boolean) value ? 1 : 0; if (c == Character.class) return (Character) value; return Long.parseLong(stringValue(value, true)); } /** * Evaluates the given object as a double-precision floating-point number. * * @param value an object to interpret as a double * @return the double value implied by the given object * @throws NumberFormatException if the given object can't be understood as a double */ public static double doubleValue(Object value) throws NumberFormatException { if (value == null) return 0.0; Class c = value.getClass(); if (c.getSuperclass() == Number.class) return ((Number) value).doubleValue(); if (c == Boolean.class) return (Boolean) value ? 1 : 0; if (c == Character.class) return (Character) value; String s = stringValue(value, true); return (s.length() == 0) ? 0.0 : Double.parseDouble(s); } /** * Evaluates the given object as a BigInteger. * * @param value an object to interpret as a BigInteger * @return the BigInteger value implied by the given object * @throws NumberFormatException if the given object can't be understood as a BigInteger */ public static BigInteger bigIntValue(Object value) throws NumberFormatException { if (value == null) return BigInteger.valueOf(0L); Class c = value.getClass(); if (c == BigInteger.class) return (BigInteger) value; if (c == BigDecimal.class) return ((BigDecimal) value).toBigInteger(); if (c.getSuperclass() == Number.class) return BigInteger.valueOf(((Number) value).longValue()); if (c == Boolean.class) return BigInteger.valueOf((Boolean) value ? 1 : 0); if (c == Character.class) return BigInteger.valueOf((Character) value); return new BigInteger(stringValue(value, true)); } /** * Evaluates the given object as a BigDecimal. * * @param value an object to interpret as a BigDecimal * @return the BigDecimal value implied by the given object * @throws NumberFormatException if the given object can't be understood as a BigDecimal */ public static BigDecimal bigDecValue(Object value) throws NumberFormatException { if (value == null) return BigDecimal.valueOf(0L); Class c = value.getClass(); if (c == BigDecimal.class) return (BigDecimal) value; if (c == BigInteger.class) return new BigDecimal((BigInteger) value); if (c == Boolean.class) return BigDecimal.valueOf((Boolean) value ? 1 : 0); if (c == Character.class) return BigDecimal.valueOf((Character) value); return new BigDecimal(stringValue(value, true)); } /** * Evaluates the given object as a String and trims it if the trim flag is true. * * @param value an object to interpret as a String * @param trim true if result should be whitespace-trimmed, false otherwise. * @return the String value implied by the given object as returned by the toString() method, or * "null" if the object is null. */ public static String stringValue(Object value, boolean trim) { String result; if (value == null) { result = OgnlRuntime.NULL_STRING; } else { result = value.toString(); if (trim) { result = result.trim(); } } return result; } /** * Evaluates the given object as a String. * * @param value an object to interpret as a String * @return the String value implied by the given object as returned by the toString() method, or * "null" if the object is null. */ public static String stringValue(Object value) { return stringValue(value, false); } /** * Returns a constant from the NumericTypes interface that represents the numeric type of the * given object. * * @param value an object that needs to be interpreted as a number * @return the appropriate constant from the NumericTypes interface */ public static int getNumericType(Object value) { if (value != null) { Class c = value.getClass(); if (c == Integer.class) return INT; if (c == Double.class) return DOUBLE; if (c == Boolean.class) return BOOL; if (c == Byte.class) return BYTE; if (c == Character.class) return CHAR; if (c == Short.class) return SHORT; if (c == Long.class) return LONG; if (c == Float.class) return FLOAT; if (c == BigInteger.class) return BIGINT; if (c == BigDecimal.class) return BIGDEC; } return NONNUMERIC; } public static Object toArray(char value, Class toType) { return toArray((Character) value, toType); } public static Object toArray(byte value, Class toType) { return toArray((Byte) value, toType); } public static Object toArray(int value, Class toType) { return toArray((Integer) value, toType); } public static Object toArray(long value, Class toType) { return toArray((Long) value, toType); } public static Object toArray(float value, Class toType) { return toArray((Float) value, toType); } public static Object toArray(double value, Class toType) { return toArray((Double) value, toType); } public static Object toArray(boolean value, Class toType) { return toArray(Boolean.valueOf(value), toType); } public static Object convertValue(char value, Class toType) { return convertValue((Character) value, toType); } public static Object convertValue(byte value, Class toType) { return convertValue((Byte) value, toType); } public static Object convertValue(int value, Class toType) { return convertValue((Integer) value, toType); } public static Object convertValue(long value, Class toType) { return convertValue((Long) value, toType); } public static Object convertValue(float value, Class toType) { return convertValue((Float) value, toType); } public static Object convertValue(double value, Class toType) { return convertValue((Double) value, toType); } public static Object convertValue(boolean value, Class toType) { return convertValue(Boolean.valueOf(value), toType); } public static Object convertValue(char value, Class toType, boolean preventNull) { return convertValue((Character) value, toType, preventNull); } public static Object convertValue(byte value, Class toType, boolean preventNull) { return convertValue((Byte) value, toType, preventNull); } public static Object convertValue(int value, Class toType, boolean preventNull) { return convertValue((Integer) value, toType, preventNull); } public static Object convertValue(long value, Class toType, boolean preventNull) { return convertValue((Long) value, toType, preventNull); } public static Object convertValue(float value, Class toType, boolean preventNull) { return convertValue((Float) value, toType, preventNull); } public static Object convertValue(double value, Class toType, boolean preventNull) { return convertValue((Double) value, toType, preventNull); } public static Object convertValue(boolean value, Class toType, boolean preventNull) { return convertValue(Boolean.valueOf(value), toType, preventNull); } public static Object toArray(char value, Class toType, boolean preventNull) { return toArray((Character) value, toType, preventNull); } public static Object toArray(byte value, Class toType, boolean preventNull) { return toArray((Byte) value, toType, preventNull); } public static Object toArray(int value, Class toType, boolean preventNull) { return toArray((Integer) value, toType, preventNull); } public static Object toArray(long value, Class toType, boolean preventNull) { return toArray((Long) value, toType, preventNull); } public static Object toArray(float value, Class toType, boolean preventNull) { return toArray((Float) value, toType, preventNull); } public static Object toArray(double value, Class toType, boolean preventNull) { return toArray((Double) value, toType, preventNull); } public static Object toArray(boolean value, Class toType, boolean preventNull) { return toArray(Boolean.valueOf(value), toType, preventNull); } /** * Returns the value converted numerically to the given class type This method also detects when * arrays are being converted and converts the components of one array to the type of the other. * * @param value an object to be converted to the given type * @param toType class type to be converted to * @return converted value of the type given, or value if the value cannot be converted to the * given type. */ public static Object convertValue(Object value, Class toType) { return convertValue(value, toType, false); } public static Object toArray(Object value, Class toType) { return toArray(value, toType, false); } public static Object toArray(Object value, Class toType, boolean preventNulls) { if (value == null) return null; Object result; if (value.getClass().isArray() && toType.isAssignableFrom(value.getClass().getComponentType())) return value; if (!value.getClass().isArray()) { if (toType == Character.TYPE) return stringValue(value).toCharArray(); if (value instanceof Collection) return ((Collection) value).toArray((Object[]) Array.newInstance(toType, 0)); Object arr = Array.newInstance(toType, 1); Array.set(arr, 0, convertValue(value, toType, preventNulls)); return arr; } result = Array.newInstance(toType, Array.getLength(value)); for (int i = 0, icount = Array.getLength(value); i < icount; i++) { Array.set(result, i, convertValue(Array.get(value, i), toType)); } return result; } public static Object convertValue(Object value, Class toType, boolean preventNulls) { Object result = null; if (value != null && toType.isAssignableFrom(value.getClass())) return value; if (value != null) { /* If array -> array then convert components of array individually */ if (value.getClass().isArray() && toType.isArray()) { Class componentType = toType.getComponentType(); result = Array.newInstance(componentType, Array.getLength(value)); for (int i = 0, icount = Array.getLength(value); i < icount; i++) { Array.set(result, i, convertValue(Array.get(value, i), componentType)); } } else if (value.getClass().isArray()) { return convertValue(Array.get(value, 0), toType); } else if (toType.isArray()) { if (toType.getComponentType() == Character.TYPE) { result = stringValue(value).toCharArray(); } else if (toType.getComponentType() == Object.class) { if (value instanceof Collection vc) { return vc.toArray(new Object[0]); } else return new Object[]{value}; } } else { if ((toType == Integer.class) || (toType == Integer.TYPE)) { result = (int) longValue(value); } if ((toType == Double.class) || (toType == Double.TYPE)) result = doubleValue(value); if ((toType == Boolean.class) || (toType == Boolean.TYPE)) result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE; if ((toType == Byte.class) || (toType == Byte.TYPE)) result = (byte) longValue(value); if ((toType == Character.class) || (toType == Character.TYPE)) result = (char) longValue(value); if ((toType == Short.class) || (toType == Short.TYPE)) result = (short) longValue(value); if ((toType == Long.class) || (toType == Long.TYPE)) result = longValue(value); if ((toType == Float.class) || (toType == Float.TYPE)) result = (float) doubleValue(value); if (toType == BigInteger.class) result = bigIntValue(value); if (toType == BigDecimal.class) result = bigDecValue(value); if (toType == String.class) result = stringValue(value); } } else { if (toType.isPrimitive()) { result = OgnlRuntime.getPrimitiveDefaultValue(toType); } else if (preventNulls && toType == Boolean.class) { result = Boolean.FALSE; } else if (preventNulls && Number.class.isAssignableFrom(toType)) { result = OgnlRuntime.getNumericDefaultValue(toType); } } if (result == null && preventNulls) return value; if (value != null && result == null) { throw new IllegalArgumentException("Unable to convert type " + value.getClass().getName() + " of " + value + " to type of " + toType.getName()); } return result; } /** * Converts the specified value to a primitive integer value. * *
    *
  • Null values will cause a -1 to be returned.
  • *
  • {@link Number} instances have their intValue() methods invoked.
  • *
  • All other types result in calling Integer.parseInt(value.toString());
  • *
* * @param value The object to get the value of. * @return A valid integer. */ public static int getIntValue(Object value) { try { if (value == null) return -1; if (value instanceof Number) { return ((Number) value).intValue(); } String str = value instanceof String ? (String) value : value.toString(); return Integer.parseInt(str); } catch (Throwable t) { throw new RuntimeException("Error converting " + value + " to integer:", t); } } /** * Returns the constant from the NumericTypes interface that best expresses the type of a * numeric operation on the two given objects. * * @param v1 one argument to a numeric operator * @param v2 the other argument * @return the appropriate constant from the NumericTypes interface */ public static int getNumericType(Object v1, Object v2) { return getNumericType(v1, v2, false); } /** * Returns the constant from the NumericTypes interface that best expresses the type of an * operation, which can be either numeric or not, on the two given types. * * @param t1 type of one argument to an operator * @param t2 type of the other argument * @param canBeNonNumeric whether the operator can be interpreted as non-numeric * @return the appropriate constant from the NumericTypes interface */ public static int getNumericType(int t1, int t2, boolean canBeNonNumeric) { if (t1 == t2) return t1; if (canBeNonNumeric && (t1 == NONNUMERIC || t2 == NONNUMERIC || t1 == CHAR || t2 == CHAR)) return NONNUMERIC; if (t1 == NONNUMERIC) t1 = DOUBLE; // Try to interpret strings as doubles... if (t2 == NONNUMERIC) t2 = DOUBLE; // Try to interpret strings as doubles... if (t1 >= MIN_REAL_TYPE) { if (t2 >= MIN_REAL_TYPE) return Math.max(t1, t2); if (t2 < INT) return t1; if (t2 == BIGINT) return BIGDEC; return Math.max(DOUBLE, t1); } else if (t2 >= MIN_REAL_TYPE) { if (t1 < INT) return t2; if (t1 == BIGINT) return BIGDEC; return Math.max(DOUBLE, t2); } else return Math.max(t1, t2); } /** * Returns the constant from the NumericTypes interface that best expresses the type of an * operation, which can be either numeric or not, on the two given objects. * * @param v1 one argument to an operator * @param v2 the other argument * @param canBeNonNumeric whether the operator can be interpreted as non-numeric * @return the appropriate constant from the NumericTypes interface */ public static int getNumericType(Object v1, Object v2, boolean canBeNonNumeric) { return getNumericType(getNumericType(v1), getNumericType(v2), canBeNonNumeric); } /** * Returns a new Number object of an appropriate type to hold the given integer value. The type * of the returned object is consistent with the given type argument, which is a constant from * the NumericTypes interface. * * @param type the nominal numeric type of the result, a constant from the NumericTypes interface * @param value the integer value to convert to a Number object * @return a Number object with the given value, of type implied by the type argument */ public static Number newInteger(int type, long value) { switch (type) { case BOOL: case CHAR: case INT: return (int) value; case FLOAT: if ((long) (float) value == value) { return (float) value; } // else fall through: case DOUBLE: if ((long) (double) value == value) { return (double) value; } // else fall through: case LONG: return value; case BYTE: return (byte) value; case SHORT: return (short) value; default: return BigInteger.valueOf(value); } } /** * Returns a new Number object of an appropriate type to hold the given real value. The type of * the returned object is always either Float or Double, and is only Float if the given type tag * (a constant from the NumericTypes interface) is FLOAT. * * @param type the nominal numeric type of the result, a constant from the NumericTypes interface * @param value the real value to convert to a Number object * @return a Number object with the given value, of type implied by the type argument */ public static Number newReal(int type, double value) { if (type == FLOAT) return (float) value; return value; } public static Object binaryOr(Object v1, Object v2) { int type = getNumericType(v1, v2); if (type == BIGINT || type == BIGDEC) return bigIntValue(v1).or(bigIntValue(v2)); return newInteger(type, longValue(v1) | longValue(v2)); } public static Object binaryXor(Object v1, Object v2) { int type = getNumericType(v1, v2); if (type == BIGINT || type == BIGDEC) return bigIntValue(v1).xor(bigIntValue(v2)); return newInteger(type, longValue(v1) ^ longValue(v2)); } public static Object binaryAnd(Object v1, Object v2) { int type = getNumericType(v1, v2); if (type == BIGINT || type == BIGDEC) return bigIntValue(v1).and(bigIntValue(v2)); return newInteger(type, longValue(v1) & longValue(v2)); } public static boolean equal(Object v1, Object v2) { if (v1 == null) return v2 == null; return v1 == v2 || isEqual(v1, v2); } public static boolean less(Object v1, Object v2) { return compareWithConversion(v1, v2) < 0; } public static boolean greater(Object v1, Object v2) { return compareWithConversion(v1, v2) > 0; } public static boolean in(Object v1, Object v2) throws OgnlException { if (v2 == null) // A null collection is always treated as empty return false; ElementsAccessor elementsAccessor = OgnlRuntime.getElementsAccessor(OgnlRuntime.getTargetClass(v2)); for (Enumeration e = elementsAccessor.getElements(v2); e.hasMoreElements(); ) { Object o = e.nextElement(); if (equal(v1, o)) return true; } return false; } public static Object shiftLeft(Object v1, Object v2) { int type = getNumericType(v1); if (type == BIGINT || type == BIGDEC) return bigIntValue(v1).shiftLeft((int) longValue(v2)); return newInteger(type, longValue(v1) << (int) longValue(v2)); } public static Object shiftRight(Object v1, Object v2) { int type = getNumericType(v1); if (type == BIGINT || type == BIGDEC) return bigIntValue(v1).shiftRight((int) longValue(v2)); return newInteger(type, longValue(v1) >> (int) longValue(v2)); } public static Object unsignedShiftRight(Object v1, Object v2) { int type = getNumericType(v1); if (type == BIGINT || type == BIGDEC) return bigIntValue(v1).shiftRight((int) longValue(v2)); if (type <= INT) return newInteger(INT, ((int) longValue(v1)) >>> (int) longValue(v2)); return newInteger(type, longValue(v1) >>> (int) longValue(v2)); } public static Object add(Object v1, Object v2) { int type = getNumericType(v1, v2, true); switch (type) { case BIGINT: return bigIntValue(v1).add(bigIntValue(v2)); case BIGDEC: return bigDecValue(v1).add(bigDecValue(v2)); case FLOAT: case DOUBLE: return newReal(type, doubleValue(v1) + doubleValue(v2)); case NONNUMERIC: int t1 = getNumericType(v1), t2 = getNumericType(v2); if (((t1 != NONNUMERIC) && (v2 == null)) || ((t2 != NONNUMERIC) && (v1 == null))) { throw new NullPointerException("Can't add values " + v1 + " , " + v2); } return stringValue(v1) + stringValue(v2); default: return newInteger(type, longValue(v1) + longValue(v2)); } } public static Object subtract(Object v1, Object v2) { int type = getNumericType(v1, v2); return switch (type) { case BIGINT -> bigIntValue(v1).subtract(bigIntValue(v2)); case BIGDEC -> bigDecValue(v1).subtract(bigDecValue(v2)); case FLOAT, DOUBLE -> newReal(type, doubleValue(v1) - doubleValue(v2)); default -> newInteger(type, longValue(v1) - longValue(v2)); }; } public static Object multiply(Object v1, Object v2) { int type = getNumericType(v1, v2); return switch (type) { case BIGINT -> bigIntValue(v1).multiply(bigIntValue(v2)); case BIGDEC -> bigDecValue(v1).multiply(bigDecValue(v2)); case FLOAT, DOUBLE -> newReal(type, doubleValue(v1) * doubleValue(v2)); default -> newInteger(type, longValue(v1) * longValue(v2)); }; } public static Object divide(Object v1, Object v2) { int type = getNumericType(v1, v2); return switch (type) { case BIGINT -> bigIntValue(v1).divide(bigIntValue(v2)); case BIGDEC -> bigDecValue(v1).divide(bigDecValue(v2), RoundingMode.HALF_EVEN); case FLOAT, DOUBLE -> newReal(type, doubleValue(v1) / doubleValue(v2)); default -> newInteger(type, longValue(v1) / longValue(v2)); }; } public static Object remainder(Object v1, Object v2) { int type = getNumericType(v1, v2); return switch (type) { case BIGDEC, BIGINT -> bigIntValue(v1).remainder(bigIntValue(v2)); default -> newInteger(type, longValue(v1) % longValue(v2)); }; } public static Object negate(Object value) { int type = getNumericType(value); return switch (type) { case BIGINT -> bigIntValue(value).negate(); case BIGDEC -> bigDecValue(value).negate(); case FLOAT, DOUBLE -> newReal(type, -doubleValue(value)); default -> newInteger(type, -longValue(value)); }; } public static Object bitNegate(Object value) { int type = getNumericType(value); return switch (type) { case BIGDEC, BIGINT -> bigIntValue(value).not(); default -> newInteger(type, ~longValue(value)); }; } public static String getEscapeString(String value) { StringBuilder result = new StringBuilder(); for (int i = 0, icount = value.length(); i < icount; i++) { result.append(getEscapedChar(value.charAt(i))); } return new String(result); } public static String getEscapedChar(char ch) { String result; switch (ch) { case '\b': result = "\b"; break; case '\t': result = "\\t"; break; case '\n': result = "\\n"; break; case '\f': result = "\\f"; break; case '\r': result = "\\r"; break; case '\"': result = "\\\""; break; case '\'': result = "\\\'"; break; case '\\': result = "\\\\"; break; default: if (Character.isISOControl(ch)) { String hc = Integer.toString(ch, 16); int hcl = hc.length(); result = "\\u"; if (hcl < 4) { if (hcl == 3) { result = result + "0"; } else { if (hcl == 2) { result = result + "00"; } else { result = result + "000"; } } } result = result + hc; } else { result = ch + ""; } break; } return result; } public static Object returnValue(Object ignore, Object returnValue) { return returnValue; } /** * Utility method that converts incoming exceptions to {@link RuntimeException} * instances - or casts them if they already are. * * @param t The exception to cast. * @return The exception cast to a {@link RuntimeException}. */ public static RuntimeException castToRuntime(Throwable t) { if (t instanceof RuntimeException) return (RuntimeException) t; if (t instanceof OgnlException) throw new UnsupportedCompilationException("Error evaluating expression: " + t.getMessage(), t); return new RuntimeException(t); } } ================================================ FILE: ognl/src/main/java/ognl/OgnlParserTreeConstants.java ================================================ /* Generated By:JavaCC: Do not edit this line. OgnlParserTreeConstants.java Version 4.1d1 */ package ognl; public interface OgnlParserTreeConstants { public int JJTVOID = 0; public int JJTSEQUENCE = 1; public int JJTASSIGN = 2; public int JJTTEST = 3; public int JJTOR = 4; public int JJTAND = 5; public int JJTBITOR = 6; public int JJTXOR = 7; public int JJTBITAND = 8; public int JJTEQ = 9; public int JJTNOTEQ = 10; public int JJTLESS = 11; public int JJTGREATER = 12; public int JJTLESSEQ = 13; public int JJTGREATEREQ = 14; public int JJTIN = 15; public int JJTNOTIN = 16; public int JJTSHIFTLEFT = 17; public int JJTSHIFTRIGHT = 18; public int JJTUNSIGNEDSHIFTRIGHT = 19; public int JJTADD = 20; public int JJTSUBTRACT = 21; public int JJTMULTIPLY = 22; public int JJTDIVIDE = 23; public int JJTREMAINDER = 24; public int JJTNEGATE = 25; public int JJTBITNEGATE = 26; public int JJTNOT = 27; public int JJTINSTANCEOF = 28; public int JJTCHAIN = 29; public int JJTEVAL = 30; public int JJTCONST = 31; public int JJTTHISVARREF = 32; public int JJTROOTVARREF = 33; public int JJTVARREF = 34; public int JJTLIST = 35; public int JJTMAP = 36; public int JJTKEYVALUE = 37; public int JJTSTATICFIELD = 38; public int JJTCTOR = 39; public int JJTPROPERTY = 40; public int JJTSTATICMETHOD = 41; public int JJTMETHOD = 42; public int JJTPROJECT = 43; public int JJTSELECT = 44; public int JJTSELECTFIRST = 45; public int JJTSELECTLAST = 46; public String[] jjtNodeName = { "void", "Sequence", "Assign", "Test", "Or", "And", "BitOr", "Xor", "BitAnd", "Eq", "NotEq", "Less", "Greater", "LessEq", "GreaterEq", "In", "NotIn", "ShiftLeft", "ShiftRight", "UnsignedShiftRight", "Add", "Subtract", "Multiply", "Divide", "Remainder", "Negate", "BitNegate", "Not", "Instanceof", "Chain", "Eval", "Const", "ThisVarRef", "RootVarRef", "VarRef", "List", "Map", "KeyValue", "StaticField", "Ctor", "Property", "StaticMethod", "Method", "Project", "Select", "SelectFirst", "SelectLast", }; } /* JavaCC - OriginalChecksum=effe3edc2df093c4b217c61a43612af5 (do not edit this line) */ ================================================ FILE: ognl/src/main/java/ognl/OgnlRuntime.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionCompiler; import ognl.enhance.OgnlExpressionCompiler; import ognl.internal.CacheException; import ognl.internal.entry.DeclaredMethodCacheEntry; import ognl.internal.entry.GenericMethodParameterTypeCacheEntry; import java.beans.BeanInfo; import java.beans.IndexedPropertyDescriptor; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Utility class used by internal OGNL API to do various things like: * *
    *
  • Handles majority of reflection logic / caching.
  • *
  • Utility methods for casting strings / various numeric types used by {@link OgnlExpressionCompiler}.
  • *
  • Core runtime configuration point for setting/using global {@link TypeConverter} / {@link OgnlExpressionCompiler} / * {@link NullHandler} instances / etc..
  • *
*/ public class OgnlRuntime { /** * Constant expression used to indicate that a given method / property couldn't be found * during reflection operations. */ public static final Object NotFound = new Object(); public static final Object[] NoArguments = new Object[]{}; /** * Token returned by TypeConverter for no conversion possible */ public static final Object NoConversionPossible = "ognl.NoConversionPossible"; /** * Not an indexed property */ public static int INDEXED_PROPERTY_NONE = 0; /** * JavaBeans IndexedProperty */ public static int INDEXED_PROPERTY_INT = 1; /** * OGNL ObjectIndexedProperty */ public static int INDEXED_PROPERTY_OBJECT = 2; /** * Constant string representation of null string. */ public static final String NULL_STRING = "" + null; /** * Java beans standard set method prefix. */ public static final String SET_PREFIX = "set"; /** * Java beans standard get method prefix. */ public static final String GET_PREFIX = "get"; /** * Java beans standard {@code is} boolean getter prefix. */ public static final String IS_PREFIX = "is"; /** * Prefix padding for hexadecimal numbers to HEX_LENGTH. */ private static final Map HEX_PADDING = new HashMap<>(); private static final int HEX_LENGTH = 8; /** * Returned by getUniqueDescriptor() when the object is null. */ private static final String NULL_OBJECT_STRING = ""; /** * Control usage of "stricter" invocation processing by invokeMethod() using the JVM options: * -Dognl.UseStricterInvocation=true * -Dognl.UseStricterInvocation=false *

* Note: Using the "true" value has the same effect as omitting the option completely. * The default behaviour is to use the "stricter" invocation processing. * Using the "false" value reverts to the older "less strict" invocation processing * (in the event the "stricter" processing causes issues for existing applications). */ static final String USE_STRICTER_INVOCATION = "ognl.UseStricterInvocation"; /** * Hold environment flag state associated with USE_STRICTER_INVOCATION. * Default: true (if not set) */ private static final boolean _useStricterInvocation; static { boolean initialFlagState = true; try { final String propertyString = System.getProperty(USE_STRICTER_INVOCATION); if (propertyString != null && !propertyString.isEmpty()) { initialFlagState = Boolean.parseBoolean(propertyString); } } catch (Exception ex) { // Unavailable (SecurityException, etc.) } _useStricterInvocation = initialFlagState; } /* * Attempt to detect the system-reported Major Java Version (e.g. 5, 7, 11). */ private static final int _majorJavaVersion = detectMajorJavaVersion(); private static final boolean _jdk9Plus = _majorJavaVersion >= 9; /** * Check if a class is sun.misc.Unsafe or jdk.internal.misc.Unsafe. * Used by stricter invocation mode to block OGNL expressions from invoking Unsafe methods. */ private static boolean isUnsafeClass(final Class clazz) { String className = clazz.getName(); return "sun.misc.Unsafe".equals(className) || "jdk.internal.misc.Unsafe".equals(className); } private static final AccessibleObjectHandler _accessibleObjectHandler = new AccessibleObjectHandler() {}; /** * Private references for use in blocking direct invocation by invokeMethod(). */ private static final Method SYS_CONSOLE_REF; private static final Method SYS_EXIT_REF; private static final Method AO_SETACCESSIBLE_REF; private static final Method AO_SETACCESSIBLE_ARR_REF; /* * Initialize the Method references used for blocking usage within invokeMethod(). */ static { Method setAccessibleMethod = null; Method setAccessibleMethodArray = null; Method systemExitMethod = null; Method systemConsoleMethod = null; try { setAccessibleMethod = AccessibleObject.class.getMethod("setAccessible", boolean.class); } catch (NoSuchMethodException nsme) { // Should not happen. To debug, uncomment the next line. //throw new IllegalStateException("OgnlRuntime initialization missing setAccessible method", nsme); } catch (SecurityException se) { // To debug, uncomment the next line. //throw new SecurityException("OgnlRuntime initialization cannot access setAccessible method", se); } finally { AO_SETACCESSIBLE_REF = setAccessibleMethod; } try { setAccessibleMethodArray = AccessibleObject.class.getMethod("setAccessible", AccessibleObject[].class, boolean.class); } catch (NoSuchMethodException nsme) { // Should not happen. To debug, uncomment the next line. //throw new IllegalStateException("OgnlRuntime initialization missing setAccessible method", nsme); } catch (SecurityException se) { // To debug, uncomment the next line. //throw new SecurityException("OgnlRuntime initialization cannot access setAccessible method", se); } finally { AO_SETACCESSIBLE_ARR_REF = setAccessibleMethodArray; } try { systemExitMethod = System.class.getMethod("exit", int.class); } catch (NoSuchMethodException nsme) { // Should not happen. To debug, uncomment the next line. //throw new IllegalStateException("OgnlRuntime initialization missing exit method", nsme); } catch (SecurityException se) { // To debug, uncomment the next line. //throw new SecurityException("OgnlRuntime initialization cannot access exit method", se); } finally { SYS_EXIT_REF = systemExitMethod; } try { systemConsoleMethod = System.class.getMethod("console"); // Not available in JDK 1.5 or earlier } catch (NoSuchMethodException nsme) { // May happen for JDK 1.5 and earlier. To debug, uncomment the next line. //throw new IllegalStateException("OgnlRuntime initialization missing console method", nsme); } catch (SecurityException se) { // To debug, uncomment the next line. //throw new SecurityException("OgnlRuntime initialization cannot access console method", se); } finally { SYS_CONSOLE_REF = systemConsoleMethod; } } /** * Allow users to revert to the old "first match" lookup for getters/setters by OGNL using the JVM options: * -Dognl.UseFirstMatchGetSetLookup=true * -Dognl.UseFirstMatchGetSetLookup=false *

* Note: Using the "false" value has the same effect as omitting the option completely. * The default behaviour is to use the "best match" lookup for getters/setters. * Using the "true" value reverts to the older "first match" lookup for getters/setters * (in the event the "best match" processing causes issues for existing applications). */ static final String USE_FIRSTMATCH_GETSET_LOOKUP = "ognl.UseFirstMatchGetSetLookup"; /** * Hold environment flag state associated with USE_FIRSTMATCH_GETSET_LOOKUP. * Default: false (if not set) */ private static final boolean _useFirstMatchGetSetLookup; static { boolean initialFlagState = false; try { final String propertyString = System.getProperty(USE_FIRSTMATCH_GETSET_LOOKUP); if (propertyString != null && !propertyString.isEmpty()) { initialFlagState = Boolean.parseBoolean(propertyString); } } catch (Exception ex) { // Unavailable (SecurityException, etc.) } _useFirstMatchGetSetLookup = initialFlagState; } static final OgnlCache cache = new OgnlCache(); private static final PrimitiveTypes primitiveTypes = new PrimitiveTypes(); private static final PrimitiveDefaults primitiveDefaults = new PrimitiveDefaults(); static final EvaluationPool _evaluationPool = new EvaluationPool(); static final Map _methodAccessCache = new ConcurrentHashMap<>(); static final Map _methodPermCache = new ConcurrentHashMap<>(); static final ClassPropertyMethodCache cacheSetMethod = new ClassPropertyMethodCache(); static final ClassPropertyMethodCache cacheGetMethod = new ClassPropertyMethodCache(); /** * Expression compiler used by {@link Ognl#compileExpression(OgnlContext, Object, String)} calls. */ private static OgnlExpressionCompiler _compiler; /** * Used to provide primitive type equivalent conversions into and out of native / object types. */ private static final PrimitiveWrapperClasses primitiveWrapperClasses = new PrimitiveWrapperClasses(); /** * Constant strings for casting different primitive types. */ private static final NumericCasts numericCasts = new NumericCasts(); /** * Constant strings for getting the primitive value of different native types on the generic {@link Number} object * interface. (or the less generic BigDecimal/BigInteger types) */ private static final NumericValues numericValues = new NumericValues(); /** * Numeric primitive literal string expressions. */ private static final NumericLiterals numericLiterals = new NumericLiterals(); private static final NumericDefaults numericDefaults = new NumericDefaults(); /* * Lazy loading of Javassist library */ static { try { Class.forName("javassist.ClassPool"); _compiler = new ExpressionCompiler(); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Javassist library is missing in classpath! Please add missed dependency!", e); } catch (RuntimeException rt) { throw new IllegalStateException("Javassist library cannot be loaded, is it restricted by runtime environment?"); } } private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; static { PropertyAccessor p = new ArrayPropertyAccessor(); setPropertyAccessor(Object.class, new ObjectPropertyAccessor()); setPropertyAccessor(byte[].class, p); setPropertyAccessor(short[].class, p); setPropertyAccessor(char[].class, p); setPropertyAccessor(int[].class, p); setPropertyAccessor(long[].class, p); setPropertyAccessor(float[].class, p); setPropertyAccessor(double[].class, p); setPropertyAccessor(Object[].class, p); setPropertyAccessor(List.class, new ListPropertyAccessor()); setPropertyAccessor(Map.class, new MapPropertyAccessor()); setPropertyAccessor(Set.class, new SetPropertyAccessor()); setPropertyAccessor(Iterator.class, new IteratorPropertyAccessor()); setPropertyAccessor(Enumeration.class, new EnumerationPropertyAccessor()); ElementsAccessor e = new ArrayElementsAccessor(); setElementsAccessor(Object.class, new ObjectElementsAccessor()); setElementsAccessor(byte[].class, e); setElementsAccessor(short[].class, e); setElementsAccessor(char[].class, e); setElementsAccessor(int[].class, e); setElementsAccessor(long[].class, e); setElementsAccessor(float[].class, e); setElementsAccessor(double[].class, e); setElementsAccessor(Object[].class, e); setElementsAccessor(Collection.class, new CollectionElementsAccessor()); setElementsAccessor(Map.class, new MapElementsAccessor()); setElementsAccessor(Iterator.class, new IteratorElementsAccessor()); setElementsAccessor(Enumeration.class, new EnumerationElementsAccessor()); setElementsAccessor(Number.class, new NumberElementsAccessor()); NullHandler nh = new ObjectNullHandler(); setNullHandler(Object.class, nh); setNullHandler(byte[].class, nh); setNullHandler(short[].class, nh); setNullHandler(char[].class, nh); setNullHandler(int[].class, nh); setNullHandler(long[].class, nh); setNullHandler(float[].class, nh); setNullHandler(double[].class, nh); setNullHandler(Object[].class, nh); MethodAccessor ma = new ObjectMethodAccessor(); setMethodAccessor(Object.class, ma); setMethodAccessor(byte[].class, ma); setMethodAccessor(short[].class, ma); setMethodAccessor(char[].class, ma); setMethodAccessor(int[].class, ma); setMethodAccessor(long[].class, ma); setMethodAccessor(float[].class, ma); setMethodAccessor(double[].class, ma); setMethodAccessor(Object[].class, ma); } /** * Clears all of the cached reflection information normally used * to improve the speed of expressions that operate on the same classes * or are executed multiple times. * *

* Warning: Calling this too often can be a huge performance * drain on your expressions - use with care. *

*/ public static void clearCache() { cache.clear(); } /** * Clears some additional caches used by OgnlRuntime. The existing {@link OgnlRuntime#clearCache()} * clears the standard reflection-related caches, but some applications may have need to clear * the additional caches as well. *

* Clearing the additional caches may have greater impact than the {@link OgnlRuntime#clearCache()} * method so it should only be used when the normal cache clear is insufficient. * *

* Warning: Calling this method too often can be a huge performance * drain on your expressions - use with care. *

* * @since 3.1.25 */ public static void clearAdditionalCache() { cacheSetMethod.clear(); cacheGetMethod.clear(); cache.clear(); } /** * Get the Major Java Version detected by OGNL. * * @return Detected Major Java Version, or 5 (minimum supported version for OGNL) if unable to detect. */ public static int getMajorJavaVersion() { return _majorJavaVersion; } public static String getNumericValueGetter(Class type) { return numericValues.get(type); } public static Class getPrimitiveWrapperClass(Class primitiveClass) { return primitiveWrapperClasses.get(primitiveClass); } public static String getNumericCast(Class type) { return numericCasts.get(type); } public static String getNumericLiteral(Class type) { return numericLiterals.get(type); } public static void setCompiler(OgnlExpressionCompiler compiler) { _compiler = compiler; } public static OgnlExpressionCompiler getCompiler() { return _compiler; } public static > void compileExpression(C context, Node expression, Object root) throws Exception { _compiler.compileExpression(context, expression, root); } /** * Gets the "target" class of an object for looking up accessors that are registered on the * target. If the object is a Class object this will return the Class itself, else it will * return object's getClass() result. * * @param o the Object from which to retrieve its Class. * @return the Class of o. */ public static Class getTargetClass(Object o) { return (o == null) ? null : ((o instanceof Class) ? (Class) o : o.getClass()); } /** * Returns the base name (the class name without the package name prepended) of the object * given. * * @param o the Object from which to retrieve its base classname. * @return the base classname of o's Class. */ public static String getBaseName(Object o) { return (o == null) ? null : getClassBaseName(o.getClass()); } /** * Returns the base name (the class name without the package name prepended) of the class given. * * @param c the Class from which to retrieve its name. * @return the base classname of c. */ public static String getClassBaseName(Class c) { String s = c.getName(); return s.substring(s.lastIndexOf('.') + 1); } public static String getClassName(Object o, boolean fullyQualified) { if (!(o instanceof Class)) { o = o.getClass(); } return getClassName((Class) o, fullyQualified); } public static String getClassName(Class c, boolean fullyQualified) { return fullyQualified ? c.getName() : getClassBaseName(c); } /** * Returns the package name of the object's class. * * @param o the Object from which to retrieve its Class package name. * @return the package name of o's Class. */ public static String getPackageName(Object o) { return (o == null) ? null : getClassPackageName(o.getClass()); } /** * Returns the package name of the class given. * * @param c the Class from which to retrieve its package name. * @return the package name of c. */ public static String getClassPackageName(Class c) { String s = c.getName(); int i = s.lastIndexOf('.'); return (i < 0) ? null : s.substring(0, i); } /** * Returns a "pointer" string in the usual format for these things - 0x<hex digits>. * * @param num the int to convert into a "pointer" string in hex format. * @return the String representing num as a "pointer" string in hex format. */ public static String getPointerString(int num) { StringBuilder result = new StringBuilder(); String hex = Integer.toHexString(num), pad; Integer l = hex.length(); // result.append(HEX_PREFIX); if ((pad = HEX_PADDING.get(l)) == null) { StringBuilder pb = new StringBuilder(); pb.append("0".repeat(HEX_LENGTH - hex.length())); pad = new String(pb); HEX_PADDING.put(l, pad); } result.append(pad); result.append(hex); return new String(result); } /** * Returns a "pointer" string in the usual format for these things - 0x<hex digits> for the * object given. This will always return a unique value for each object. * * @param o the Object to convert into a "pointer" string in hex format. * @return the String representing o as a "pointer" string in hex format. */ public static String getPointerString(Object o) { return getPointerString((o == null) ? 0 : System.identityHashCode(o)); } /** * Returns a unique descriptor string that includes the object's class and a unique integer * identifier. If fullyQualified is true then the class name will be fully qualified to include * the package name, else it will be just the class' base name. * * @param object the Object for which a unique descriptor string is desired. * @param fullyQualified true if the descriptor string is fully-qualified (package name), false for just the Class' base name. * @return the unique descriptor String for the object, qualified as per fullyQualified parameter. */ public static String getUniqueDescriptor(Object object, boolean fullyQualified) { StringBuilder result = new StringBuilder(); if (object != null) { if (object instanceof Proxy) { Class interfaceClass = object.getClass().getInterfaces()[0]; result.append(getClassName(interfaceClass, fullyQualified)); result.append('^'); object = Proxy.getInvocationHandler(object); } result.append(getClassName(object, fullyQualified)); result.append('@'); result.append(getPointerString(object)); } else { result.append(NULL_OBJECT_STRING); } return new String(result); } /** * Returns a unique descriptor string that includes the object's class' base name and a unique * integer identifier. * * @param object the Object for which a unique descriptor string is desired. * @return the unique descriptor String for the object, NOT fully-qualified. */ public static String getUniqueDescriptor(Object object) { return getUniqueDescriptor(object, false); } /** * Returns the parameter types of the given method. * * @param method the Method whose parameter types are being queried. * @return the array of Class elements representing m's parameters. May be null if m does not utilize parameters. */ public static Class[] getParameterTypes(Method method) throws CacheException { return cache.getMethodParameterTypes(method); } /** * Finds the appropriate parameter types for the given {@link Method} and * {@link Class} instance of the type the method is associated with. Correctly * finds generic types if running in >= 1.5 jre as well. * * @param type The class type the method is being executed against. * @param method The method to find types for. * @return Array of parameter types for the given method. */ public static Class[] findParameterTypes(Class type, Method method) { if (type == null || type.getGenericSuperclass() == null || !(type.getGenericSuperclass() instanceof ParameterizedType)) { return getParameterTypes(method); } GenericMethodParameterTypeCacheEntry key = new GenericMethodParameterTypeCacheEntry(method, type); return cache.getGenericMethodParameterTypes(key); } /** * Returns the parameter types of the given method. * * @param constructor the Constructor whose parameter types are being queried. * @return the array of Class elements representing c's parameters. May be null if c does not utilize parameters. */ public static Class[] getParameterTypes(Constructor constructor) throws CacheException { return cache.getParameterTypes(constructor); } public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException { boolean syncInvoke; Boolean methodAccessCacheValue; if (_useStricterInvocation) { final Class methodDeclaringClass = method.getDeclaringClass(); // Note: synchronized(method) call below will already NPE, so no null check. if ((AO_SETACCESSIBLE_REF != null && AO_SETACCESSIBLE_REF.equals(method)) || (AO_SETACCESSIBLE_ARR_REF != null && AO_SETACCESSIBLE_ARR_REF.equals(method)) || (SYS_EXIT_REF != null && SYS_EXIT_REF.equals(method)) || (SYS_CONSOLE_REF != null && SYS_CONSOLE_REF.equals(method)) || AccessibleObjectHandler.class.isAssignableFrom(methodDeclaringClass) || ClassResolver.class.isAssignableFrom(methodDeclaringClass) || MethodAccessor.class.isAssignableFrom(methodDeclaringClass) || MemberAccess.class.isAssignableFrom(methodDeclaringClass) || OgnlContext.class.isAssignableFrom(methodDeclaringClass) || Runtime.class.isAssignableFrom(methodDeclaringClass) || ClassLoader.class.isAssignableFrom(methodDeclaringClass) || ProcessBuilder.class.isAssignableFrom(methodDeclaringClass) || isUnsafeClass(methodDeclaringClass)) { // Prevent calls to some specific methods, as well as all methods of certain classes/interfaces // for which no (apparent) legitimate use cases exist for their usage within OGNL invokeMethod(). throw new IllegalAccessException("Method [" + method + "] cannot be called from within OGNL invokeMethod() " + "under stricter invocation mode."); } } // only synchronize method invocation if it actually requires it methodAccessCacheValue = _methodAccessCache.get(method); // double null check to avoid synchronizing on the method if (methodAccessCacheValue == null) { synchronized (method) { methodAccessCacheValue = _methodAccessCache.get(method); if (methodAccessCacheValue == null) { if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) { var obj = Modifier.isStatic(method.getModifiers()) ? null : target; if (method.canAccess(obj)) { methodAccessCacheValue = Boolean.FALSE; _methodAccessCache.put(method, methodAccessCacheValue); } else { methodAccessCacheValue = Boolean.TRUE; _methodAccessCache.put(method, methodAccessCacheValue); } } else { methodAccessCacheValue = Boolean.FALSE; _methodAccessCache.put(method, methodAccessCacheValue); } } _methodPermCache.putIfAbsent(method, Boolean.TRUE); } } syncInvoke = Boolean.TRUE.equals(methodAccessCacheValue); Object result; if (syncInvoke) //if is not public and is not accessible { synchronized (method) { _accessibleObjectHandler.setAccessible(method, true); try { result = method.invoke(target, argsArray); } finally { _accessibleObjectHandler.setAccessible(method, false); } } } else { result = method.invoke(target, argsArray); } return result; } /** * Gets the class for a method argument that is appropriate for looking up methods by * reflection, by looking for the standard primitive wrapper classes and exchanging for them * their underlying primitive class objects. Other classes are passed through unchanged. * * @param arg an object that is being passed to a method * @return the class to use to look up the method */ public static Class getArgClass(Object arg) { if (arg == null) return null; Class c = arg.getClass(); if (c == Boolean.class) return Boolean.TYPE; else if (c.getSuperclass() == Number.class) { if (c == Integer.class) return Integer.TYPE; if (c == Double.class) return Double.TYPE; if (c == Byte.class) return Byte.TYPE; if (c == Long.class) return Long.TYPE; if (c == Float.class) return Float.TYPE; if (c == Short.class) return Short.TYPE; } else if (c == Character.class) return Character.TYPE; return c; } public static Class[] getArgClasses(Object[] args) { if (args == null) return null; Class[] argClasses = new Class[args.length]; for (int i = 0; i < args.length; i++) { argClasses[i] = getArgClass(args[i]); } return argClasses; } /** * Tells whether the given object is compatible with the given class ---that is, whether the * given object can be passed as an argument to a method or constructor whose parameter type is * the given class. If object is null this will return true because null is compatible with any * type. * * @param object the Object to check for type-compatibility with Class c. * @param c the Class for which object's type-compatibility is being checked. * @return true if object is type-compatible with c. */ public static boolean isTypeCompatible(Object object, Class c) { if (object == null) return true; ArgsCompatbilityReport report = new ArgsCompatbilityReport(0, new boolean[1]); if (!isTypeCompatible(getArgClass(object), c, 0, report)) return false; return !report.conversionNeeded[0]; // we don't allow conversions during this path... } public static boolean isTypeCompatible(Class parameterClass, Class methodArgumentClass, int index, ArgsCompatbilityReport report) { if (parameterClass == null) { // happens when we cannot determine parameter... report.score += 500; return true; } if (parameterClass == methodArgumentClass) return true; // exact match, no additional score //if (methodArgumentClass.isPrimitive()) // return false; // really? int can be assigned to long... *hmm* if (methodArgumentClass.isArray()) { if (parameterClass.isArray()) { Class pct = parameterClass.getComponentType(); Class mct = methodArgumentClass.getComponentType(); if (mct.isAssignableFrom(pct)) { // two arrays are better then a array and a list or other conversions... report.score += 25; return true; } //return isTypeCompatible(pct, mct, index, report); // check inner classes } if (Collection.class.isAssignableFrom(parameterClass)) { // we have to assume that all Collections carry objects - generics access is of no use during runtime because of // Type Erasure - http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#Type%20Erasure Class mct = methodArgumentClass.getComponentType(); if (mct == Object.class) { report.conversionNeeded[index] = true; report.score += 30; return true; } else { // Okay, the items from the list *might* not match. we better don't do that... return false; } } } else if (Collection.class.isAssignableFrom(methodArgumentClass)) { if (parameterClass.isArray()) { // TODO get generics type here and do further evaluations... report.conversionNeeded[index] = true; report.score += 50; return true; } if (Collection.class.isAssignableFrom(parameterClass)) { if (methodArgumentClass.isAssignableFrom(parameterClass)) { // direct possible List assignment - good match... report.score += 2; return true; } // TODO get generics type here and do further evaluations... report.conversionNeeded[index] = true; report.score += 50; return true; } } if (methodArgumentClass.isAssignableFrom(parameterClass)) { report.score += 40; // works but might not the best match - weight of 50.. return true; } if (parameterClass.isPrimitive()) { Class ptc = primitiveWrapperClasses.get(parameterClass); if (methodArgumentClass == ptc) { report.score += 2; // quite an good match return true; } if (methodArgumentClass.isAssignableFrom(ptc)) { report.score += 10; // works but might not the best match - weight of 10.. return true; } } return false; // dosn't match. /* boolean result = true; if (parameterClass != null) { if (methodArgumentClass.isPrimitive()) { if (parameterClass != methodArgumentClass) { result = false; } } else if (!methodArgumentClass.isAssignableFrom(parameterClass)) { result = false; } } return result; */ } /** * Tells whether the given array of objects is compatible with the given array of classes---that * is, whether the given array of objects can be passed as arguments to a method or constructor * whose parameter types are the given array of classes. */ public static class ArgsCompatbilityReport { int score; boolean[] conversionNeeded; public ArgsCompatbilityReport(int score, boolean[] conversionNeeded) { this.score = score; this.conversionNeeded = conversionNeeded; } } public static final ArgsCompatbilityReport NoArgsReport = new ArgsCompatbilityReport(0, new boolean[0]); public static boolean areArgsCompatible(Object[] args, Class[] classes) { ArgsCompatbilityReport report = areArgsCompatible(getArgClasses(args), classes, null); if (report == null) return false; for (boolean conversionNeeded : report.conversionNeeded) if (conversionNeeded) return false; return true; } public static ArgsCompatbilityReport areArgsCompatible(Class[] args, Class[] classes, Method m) { boolean varArgs = m != null && m.isVarArgs(); if (args == null || args.length == 0) { // handle methods without arguments if (classes == null || classes.length == 0) { return NoArgsReport; } else { if (varArgs) { return NoArgsReport; } return null; } } if (args.length != classes.length && !varArgs) { return null; } else if (varArgs) { /* * varArg's start with a penalty of 1000. * There are some java compiler rules that are hopefully reflectet by this penalty: * * Legacy beats Varargs * * Widening beats Varargs * * Boxing beats Varargs */ ArgsCompatbilityReport report = new ArgsCompatbilityReport(1000, new boolean[args.length]); /* * varargs signature is: method(type1, type2, typeN, typeV ...) * This means: All arguments up to typeN needs exact matching, all varargs need to match typeV */ if (classes.length - 1 > args.length) // we don't have enough arguments to provide the required 'fixed' arguments return null; // type check on fixed arguments for (int index = 0, count = classes.length - 1; index < count; ++index) if (!isTypeCompatible(args[index], classes[index], index, report)) return null; // type check on varargs Class varArgsType = classes[classes.length - 1].getComponentType(); for (int index = classes.length - 1, count = args.length; index < count; ++index) if (!isTypeCompatible(args[index], varArgsType, index, report)) return null; return report; } else { ArgsCompatbilityReport report = new ArgsCompatbilityReport(0, new boolean[args.length]); for (int index = 0, count = args.length; index < count; ++index) if (!isTypeCompatible(args[index], classes[index], index, report)) return null; return report; } } /** * Tells whether the first array of classes is more specific than the second. Assumes that the * two arrays are of the same length. * * @param classes1 the Class array being checked to see if it is "more specific" than classes2. * @param classes2 the Class array that classes1 is being checked against to see if classes1 is "more specific" than classes2. * @return true if the classes1 Class contents are "more specific" than classes2 Class contents, false otherwise. */ public static boolean isMoreSpecific(Class[] classes1, Class[] classes2) { for (int index = 0, count = classes1.length; index < count; ++index) { Class c1 = classes1[index], c2 = classes2[index]; if (c1 == c2) continue; else if (c1.isPrimitive()) return true; else if (c1.isAssignableFrom(c2)) return false; else if (c2.isAssignableFrom(c1)) return true; } // They are the same! So the first is not more specific than the second. return false; } public static String getModifierString(int modifiers) { String result; if (Modifier.isPublic(modifiers)) result = "public"; else if (Modifier.isProtected(modifiers)) result = "protected"; else if (Modifier.isPrivate(modifiers)) result = "private"; else result = ""; if (Modifier.isStatic(modifiers)) result = "static " + result; if (Modifier.isFinal(modifiers)) result = "final " + result; if (Modifier.isNative(modifiers)) result = "native " + result; if (Modifier.isSynchronized(modifiers)) result = "synchronized " + result; if (Modifier.isTransient(modifiers)) result = "transient " + result; return result; } public static > Class classForName(C context, String className) throws ClassNotFoundException { Class result = primitiveTypes.get(className); if (result == null) { ClassResolver resolver; if ((context == null) || ((resolver = context.getClassResolver()) == null)) { resolver = new DefaultClassResolver(); } result = resolver.classForName(className, context); } if (result == null) throw new ClassNotFoundException("Unable to resolve class: " + className); return result; } public static > boolean isInstance(C context, Object value, String className) throws OgnlException { try { Class c = classForName(context, className); return c.isInstance(value); } catch (ClassNotFoundException e) { throw new OgnlException("No such class: " + className, e); } } public static Object getPrimitiveDefaultValue(Class forClass) { return primitiveDefaults.get(forClass); } public static Object getNumericDefaultValue(Class forClass) { return numericDefaults.get(forClass); } public static > Object getConvertedType(C context, Object target, Member member, String propertyName, Object value, Class type) { return context.getTypeConverter().convertValue(context, target, member, propertyName, value, type); } public static > boolean getConvertedTypes(C context, Object target, Member member, String propertyName, Class[] parameterTypes, Object[] args, Object[] newArgs) { boolean result = false; if (parameterTypes.length == args.length) { result = true; for (int i = 0, ilast = parameterTypes.length - 1; result && (i <= ilast); i++) { Object arg = args[i]; Class type = parameterTypes[i]; if (isTypeCompatible(arg, type)) { newArgs[i] = arg; } else { Object v = getConvertedType(context, target, member, propertyName, arg, type); if (v == OgnlRuntime.NoConversionPossible) { result = false; } else { newArgs[i] = v; } } } } return result; } public static > Constructor getConvertedConstructorAndArgs(C context, Object target, List> constructors, Object[] args, Object[] newArgs) { Constructor result = null; TypeConverter converter = context.getTypeConverter(); if ((converter != null) && (constructors != null)) { for (int i = 0, icount = constructors.size(); (result == null) && (i < icount); i++) { Constructor ctor = constructors.get(i); Class[] parameterTypes = getParameterTypes(ctor); if (getConvertedTypes(context, target, ctor, null, parameterTypes, args, newArgs)) { result = ctor; } } } return result; } /** * Gets the appropriate method to be called for the given target, method name and arguments. If * successful this method will return the Method within the target that can be called and the * converted arguments in actualArgs. If unsuccessful this method will return null and the * actualArgs will be empty. * * @param context The current execution context. * @param source Target object to run against or method name. * @param target Instance of object to be run against. * @param propertyName Name of property to get method of. * @param methodName Name of the method to get from known methods. * @param methods List of current known methods. * @param args Arguments originally passed in. * @param actualArgs Converted arguments. * @return Best method match or null if none could be found. */ public static > Method getAppropriateMethod(C context, Object source, Object target, String propertyName, String methodName, List methods, Object[] args, Object[] actualArgs) { Method result = null; if (methods != null) { Class typeClass = target != null ? target.getClass() : null; if (typeClass == null && source instanceof Class) { typeClass = (Class) source; } Class[] argClasses = getArgClasses(args); MatchingMethod mm = findBestMethod(methods, typeClass, methodName, argClasses); if (mm != null) { result = mm.mMethod; Class[] mParameterTypes = mm.mParameterTypes; System.arraycopy(args, 0, actualArgs, 0, args.length); if (actualArgs.length > 0) { for (int j = 0; j < mParameterTypes.length; j++) { Class type = mParameterTypes[j]; if (mm.report.conversionNeeded[j] || (type.isPrimitive() && (actualArgs[j] == null))) { actualArgs[j] = getConvertedType(context, source, result, propertyName, args[j], type); } } } } } if (result == null) { result = getConvertedMethodAndArgs(context, target, propertyName, methods, args, actualArgs); } return result; } public static > Method getConvertedMethodAndArgs(C context, Object target, String propertyName, List methods, Object[] args, Object[] newArgs) { Method result = null; TypeConverter converter = context.getTypeConverter(); if ((converter != null) && (methods != null)) { for (int i = 0, icount = methods.size(); (result == null) && (i < icount); i++) { Method m = methods.get(i); Class[] parameterTypes = findParameterTypes(target != null ? target.getClass() : null, m);//getParameterTypes(m); if (getConvertedTypes(context, target, m, propertyName, parameterTypes, args, newArgs)) { result = m; } } } return result; } private static class MatchingMethod { Method mMethod; int score; ArgsCompatbilityReport report; Class[] mParameterTypes; private MatchingMethod(Method method, int score, ArgsCompatbilityReport report, Class[] mParameterTypes) { this.mMethod = method; this.score = score; this.report = report; this.mParameterTypes = mParameterTypes; } } /** * Checks if a class is likely to be accessible, considering the Java module system. * This helps avoid selecting methods from internal JDK classes that are not exported. *

* Package-private for testing purposes. * * @param clazz the class to check * @return true if the class is likely accessible, false if it's likely inaccessible */ static boolean isLikelyAccessible(Class clazz) { // Interfaces are generally preferred as they represent the public contract if (clazz.isInterface()) { return true; } String packageName = clazz.getPackageName(); // Empty package name (default package) - treat as accessible if (packageName == null || packageName.isEmpty()) { return true; } // Check for known internal/unexported packages if (packageName.startsWith("sun.") || packageName.startsWith("com.sun.") || packageName.startsWith("jdk.internal.") || packageName.startsWith("java.awt.peer") || packageName.startsWith("java.dyn") || packageName.startsWith("org.jcp.xml.dsig.internal")) { return false; } // For Java 9+, check if the module exports the package try { Module module = clazz.getModule(); if (module != null && module.isNamed()) { // Check if the package is exported unconditionally // If it's not exported, the class is likely inaccessible return module.isExported(packageName); } } catch (Exception e) { // If we can't determine module info, assume it might be accessible // (better to try and fail than to skip a valid method) } // Default: assume it's accessible return true; } private static MatchingMethod findBestMethod(List methods, Class typeClass, String name, Class[] argClasses) { MatchingMethod mm = null; IllegalArgumentException failure = null; for (Method method : methods) { Class[] mParameterTypes = findParameterTypes(typeClass, method); ArgsCompatbilityReport report = areArgsCompatible(argClasses, mParameterTypes, method); if (report == null) continue; String methodName = method.getName(); int score = report.score; if (name.equals(methodName)) { // exact match - no additinal score... } else if (name.equalsIgnoreCase(methodName)) { // minimal penalty.. score += 200; } else if (methodName.toLowerCase().endsWith(name.toLowerCase())) { // has a prefix... score += 500; } else { // just in case... score += 5000; } if (mm == null || mm.score > score) { mm = new MatchingMethod(method, score, report, mParameterTypes); failure = null; } else if (mm.score == score) { // it happens that we see the same method signature multiple times - for the current class or interfaces ... // check for same signature if (Arrays.equals(mm.mMethod.getParameterTypes(), method.getParameterTypes()) && mm.mMethod.getName().equals(method.getName())) { // it is the same method. Prefer accessible ones over inaccessible ones Class currentClass = mm.mMethod.getDeclaringClass(); Class newClass = method.getDeclaringClass(); boolean currentAccessible = isLikelyAccessible(currentClass); boolean newAccessible = isLikelyAccessible(newClass); // Primary goal: prefer accessible methods over inaccessible ones (fixes issue #286) if (!currentAccessible && newAccessible) { mm = new MatchingMethod(method, score, report, mParameterTypes); failure = null; } else if (currentAccessible && !newAccessible) { // Current is accessible, new is not - keep current (no change needed) } else { // Both accessible or both inaccessible - use original tie-breaking logic // Prefer public classes if (!Modifier.isPublic(currentClass.getModifiers()) && Modifier.isPublic(newClass.getModifiers())) { mm = new MatchingMethod(method, score, report, mParameterTypes); failure = null; } } } else { // two methods with same score - direct compare to find the better one... // legacy wins over varargs if (method.isVarArgs() || mm.mMethod.isVarArgs()) { if (method.isVarArgs() && !mm.mMethod.isVarArgs()) { // keep with current } else if (!method.isVarArgs()) { // legacy wins... mm = new MatchingMethod(method, score, report, mParameterTypes); failure = null; } else { // both arguments are varargs... System.err.println("Two vararg methods with same score(" + score + "): \"" + mm.mMethod + "\" and \"" + method + "\" please report!"); } } else { int scoreCurr = 0; int scoreOther = 0; for (int j = 0; j < argClasses.length; j++) { Class argClass = argClasses[j]; Class mcClass = mm.mParameterTypes[j]; Class moClass = mParameterTypes[j]; if (argClass == null) { // TODO can we avoid this case? // we don't know the class. use the most generic implementation... if (mcClass == moClass) { // equal args - no winner... } else if (mcClass.isAssignableFrom(moClass)) { scoreOther += 1000; // current wins... } else if (moClass.isAssignableFrom(moClass)) { scoreCurr += 1000; // other wins... } else { // both items can't be assigned to each other.. failure = new IllegalArgumentException("Can't decide wich method to use: \"" + mm.mMethod + "\" or \"" + method + "\""); } } else { // we try to find the more specific implementation if (mcClass == moClass) { // equal args - no winner... } else if (mcClass == argClass) { scoreOther += 100; // current wins... } else if (moClass == argClass) { scoreCurr += 100; // other wins... } else if (mcClass.isAssignableFrom(moClass)) { scoreOther += 50; // current wins... } else if (moClass.isAssignableFrom(moClass)) { scoreCurr += 50; // other wins... } else { // both items can't be assigned to each other.. // TODO: if this happens we have to put some weight on the inheritance... failure = new IllegalArgumentException("Can't decide wich method to use: \"" + mm.mMethod + "\" or \"" + method + "\""); } } } if (scoreCurr == scoreOther) { if (failure == null) { boolean currentIsAbstract = Modifier.isAbstract(mm.mMethod.getModifiers()); boolean otherIsAbstract = Modifier.isAbstract(method.getModifiers()); if (currentIsAbstract == otherIsAbstract) { // Only report as an error when the score is equal and BOTH methods are abstract or BOTH are concrete. // If one is abstract and the other concrete then either choice should work for OGNL, // so we just keep the current choice and continue (without error output). System.err.println("Two methods with same score(" + score + "): \"" + mm.mMethod + "\" and \"" + method + "\" please report!"); } } } else if (scoreCurr > scoreOther) { // other wins... mm = new MatchingMethod(method, score, report, mParameterTypes); failure = null; } // else current one wins... } } } } if (failure != null) throw failure; return mm; } public static > Object callAppropriateMethod(C context, Object source, Object target, String methodName, String propertyName, List methods, Object[] args) throws MethodFailedException { Throwable reason; Object[] actualArgs = new Object[args.length]; try { Method method = getAppropriateMethod(context, source, target, propertyName, methodName, methods, args, actualArgs); if (!isMethodAccessible(context, source, method, propertyName)) { StringBuilder buffer = new StringBuilder(); String className = ""; if (target != null) { className = target.getClass().getName() + "."; } for (int i = 0, ilast = args.length - 1; i <= ilast; i++) { Object arg = args[i]; buffer.append((arg == null) ? NULL_STRING : arg.getClass().getName()); if (i < ilast) { buffer.append(", "); } } throw new NoSuchMethodException(className + methodName + "(" + buffer + ")"); } Object[] convertedArgs = actualArgs; if (method.isVarArgs()) { Class[] parmTypes = method.getParameterTypes(); // split arguments in to two dimensional array for varargs reflection invocation // where it is expected that the parameter passed in to invoke the method // will look like "new Object[] { arrayOfNonVarArgsArguments, arrayOfVarArgsArguments }" for (int i = 0; i < parmTypes.length; i++) { if (parmTypes[i].isArray()) { convertedArgs = new Object[i + 1]; if (actualArgs.length > 0) { System.arraycopy(actualArgs, 0, convertedArgs, 0, convertedArgs.length); } Object[] varArgs; // if they passed in varargs arguments grab them and dump in to new varargs array if (actualArgs.length > i) { List varArgsList = new ArrayList<>(); for (int j = i; j < actualArgs.length; j++) { if (actualArgs[j] != null) { varArgsList.add(actualArgs[j]); } } if (actualArgs.length == 1) { varArgs = (Object[]) Array.newInstance(args[0].getClass(), 1); } else { varArgs = (Object[]) Array.newInstance(parmTypes[i].getComponentType(), varArgsList.size()); } System.arraycopy(varArgsList.toArray(), 0, varArgs, 0, varArgs.length); } else { varArgs = new Object[0]; } // If this is the only parameter, explode the array if (actualArgs.length == 1 && args[0].getClass().isArray()) { convertedArgs = varArgs; } else { // there are more parameters, varargs is the last one convertedArgs[i] = varArgs; } break; } } } return invokeMethod(target, method, convertedArgs); } catch (NoSuchMethodException | IllegalAccessException e) { reason = e; } catch (InvocationTargetException e) { reason = e.getTargetException(); } throw new MethodFailedException(source, methodName, reason); } public static > Object callStaticMethod(C context, String className, String methodName, Object[] args) throws OgnlException { try { Class targetClass = classForName(context, className); MethodAccessor ma = getMethodAccessor(targetClass); return ma.callStaticMethod(context, targetClass, methodName, args); } catch (ClassNotFoundException ex) { throw new MethodFailedException(className, methodName, ex); } } /** * Invokes the specified method against the target object. * * @param context The current execution context. * @param target The object to invoke the method on. * @param methodName Name of the method - as in "getValue" or "add", etc.. * @param args Optional arguments needed for method. * @return Result of invoking method. * @throws OgnlException For lots of different reasons. */ public static > Object callMethod(C context, Object target, String methodName, Object[] args) throws OgnlException { if (target == null) throw new NullPointerException("target is null for method " + methodName); MethodAccessor methodAccessor = getMethodAccessor(target.getClass()); return methodAccessor.callMethod(context, target, methodName, args); } public static > Object callConstructor(C context, String className, Object[] args) throws OgnlException { Throwable reason; Object[] actualArgs = args; try { Constructor ctor = null; Class[] ctorParameterTypes = null; Class target = classForName(context, className); List> constructors = getConstructors(target); for (Constructor constructor : constructors) { Class[] cParameterTypes = getParameterTypes(constructor); if (areArgsCompatible(args, cParameterTypes) && (ctor == null || isMoreSpecific(cParameterTypes, ctorParameterTypes))) { ctor = constructor; ctorParameterTypes = cParameterTypes; } } if (ctor == null) { actualArgs = new Object[args.length]; if ((ctor = getConvertedConstructorAndArgs(context, target, constructors, args, actualArgs)) == null) { throw new NoSuchMethodException(); } } if (!isAccessible(context, target, ctor, null)) { throw new IllegalAccessException( "access denied to " + target.getName() + "()"); } return ctor.newInstance(actualArgs); } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException e) { reason = e; } catch (InvocationTargetException e) { reason = e.getTargetException(); } throw new MethodFailedException(className, "new", reason); } /** * If the checkAccessAndExistence flag is true this method will check to see if the method * exists and if it is accessible according to the context's MemberAccess. If neither test * passes this will return NotFound. * * @param context the current execution context. * @param target the object to invoke the property name get on. * @param propertyName the name of the property to be retrieved from target. * @param checkAccessAndExistence true if this method should check access levels and existence for propertyName of target, false otherwise. * @return the result invoking property retrieval of propertyName for target. * @throws OgnlException for lots of different reasons. * @throws IllegalAccessException if access not permitted. * @throws NoSuchMethodException if no property accessor exists. */ public static > Object getMethodValue(C context, Object target, String propertyName, boolean checkAccessAndExistence) throws OgnlException, IllegalAccessException, NoSuchMethodException { Object result = null; Method m = getGetMethod((target == null) ? null : target.getClass(), propertyName); if (m == null && !context.isIgnoreReadMethods()) m = getReadMethod((target == null) ? null : target.getClass(), propertyName, null); if (checkAccessAndExistence) { if ((m == null) || !isAccessible(context, target, m, propertyName)) { result = NotFound; } } if (result == null) { if (m != null) { try { result = invokeMethod(target, m, NoArguments); } catch (InvocationTargetException ex) { throw new OgnlException(propertyName, ex.getTargetException()); } } else { throw new NoSuchMethodException(propertyName); } } return result; } /** * Don't use this method as it doesn't check member access rights via {@link MemberAccess} interface * * @param context the current execution context. * @param target the object to invoke the property name get on. * @param propertyName the name of the property to be set for target. * @param value the value to set for propertyName of target. * @return true if the operation succeeded, false otherwise. * @throws OgnlException for lots of different reasons. */ public static > boolean setMethodValue(C context, Object target, String propertyName, Object value, boolean checkAccessAndExistence) throws OgnlException { boolean result = true; Method m = getSetMethod(context, (target == null) ? null : target.getClass(), propertyName); if (checkAccessAndExistence) { if ((m == null) || !isAccessible(context, target, m, propertyName)) { result = false; } } if (result) { if (m != null) { Object[] args = new Object[]{value}; callAppropriateMethod(context, target, target, m.getName(), propertyName, Collections.nCopies(1, m), args); } else { result = false; } } return result; } public static List> getConstructors(Class targetClass) { return cache.getConstructor(targetClass); } public static Map> getMethods(Class targetClass, boolean staticMethods) { DeclaredMethodCacheEntry.MethodType type = staticMethods ? DeclaredMethodCacheEntry.MethodType.STATIC : DeclaredMethodCacheEntry.MethodType.NON_STATIC; DeclaredMethodCacheEntry key = new DeclaredMethodCacheEntry(targetClass, type); return cache.getMethod(key); } /** * Backport of java.lang.reflect.Method#isDefault() *

* JDK8+ supports Default Methods for interfaces. Default Methods are defined as: * public, non-abstract and declared within an interface (must also be non-static). * * @param method The Method to check against the requirements for a Default Method. * @return true If the Method qualifies as a Default Method, false otherwise. */ private static boolean isDefaultMethod(Method method) { return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); } /** * Determine if the provided Method is a non-Default public Interface method. *

* Public non-Default Methods are defined as: * public, abstract, non-static and declared within an interface. * * @param method The Method to check against the requirements for a non-Default Method. * @return true If method qualifies as a non-Default public Interface method, false otherwise. * @since 3.1.25 */ private static boolean isNonDefaultPublicInterfaceMethod(Method method) { return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == (Modifier.ABSTRACT | Modifier.PUBLIC)) && method.getDeclaringClass().isInterface(); } public static List getMethods(Class targetClass, String name, boolean staticMethods) { return getMethods(targetClass, staticMethods).get(name); } public static Map getFields(Class targetClass) { return cache.getField(targetClass); } public static Field getField(Class inClass, String name) { Field field = getFields(inClass).get(name); if (field == null) { // if field is null, it should search along the superclasses Class superClass = inClass.getSuperclass(); while (superClass != null) { field = getFields(superClass).get(name); if (field != null) { return field; } superClass = superClass.getSuperclass(); } } return field; } /** * Don't use this method as it doesn't check member access rights via {@link MemberAccess} interface * * @param context the current execution context. * @param target the object to invoke the property name get on. * @param propertyName the name of the property to be set for target. * @return the result invoking field retrieval of propertyName for target. * @throws NoSuchFieldException if the field does not exist. */ public static > Object getFieldValue(C context, Object target, String propertyName, boolean checkAccessAndExistence) throws NoSuchFieldException { Object result = null; final Field f = getField((target == null) ? null : target.getClass(), propertyName); if (checkAccessAndExistence) { if ((f == null) || !isAccessible(context, target, f, propertyName)) { result = NotFound; } } if (result == null) { if (f == null) { throw new NoSuchFieldException(propertyName); } else { try { if (!Modifier.isStatic(f.getModifiers())) { final Object state = context.getMemberAccess().setup(context, target, f, propertyName); try { result = f.get(target); } finally { context.getMemberAccess().restore(context, target, f, propertyName, state); } } else { throw new NoSuchFieldException(propertyName); } } catch (IllegalAccessException ex) { throw new NoSuchFieldException(propertyName); } } } return result; } /** * Don't use this method as it doesn't check member access rights via {@link MemberAccess} interface */ public static > boolean setFieldValue(C context, Object target, String propertyName, Object value, boolean checkAccessAndExistence) throws OgnlException { boolean result = false; try { final Field f = getField((target == null) ? null : target.getClass(), propertyName); if (f != null) { final int fModifiers = f.getModifiers(); if (!Modifier.isStatic(fModifiers) && !Modifier.isFinal(fModifiers) && (!checkAccessAndExistence || isAccessible(context, target, f, propertyName))) { final Object state = context.getMemberAccess().setup(context, target, f, propertyName); try { if (isTypeCompatible(value, f.getType()) || ((value = getConvertedType(context, target, f, propertyName, value, f.getType())) != null)) { f.set(target, value); result = true; } } finally { context.getMemberAccess().restore(context, target, f, propertyName, state); } } } } catch (IllegalAccessException ex) { throw new NoSuchPropertyException(target, propertyName, ex); } return result; } public static > boolean isFieldAccessible(C context, Object target, Class inClass, String propertyName) { return isFieldAccessible(context, target, getField(inClass, propertyName), propertyName); } public static > boolean isFieldAccessible(C context, Object target, Field field, String propertyName) { return isAccessible(context, target, field, propertyName); } public static > boolean hasField(C context, Object target, Class inClass, String propertyName) { Field f = getField(inClass, propertyName); return (f != null) && isFieldAccessible(context, target, f, propertyName); } /** * Method name is getStaticField(), but actually behaves more like "getStaticFieldValue()". *

* Typical usage: Returns the value (not the actual {@link Field}) for the given (static) fieldName. * May return the {@link Enum} constant value for the given fieldName when className is an {@link Enum}. * May return a {@link Class} instance when the given fieldName is "class". *

* * @param context The current ognl context * @param className The name of the class which contains the field * @param fieldName The name of the field whose value should be returned * @return The value of the (static) fieldName * @throws OgnlException for lots of different reasons. */ public static > Object getStaticField(C context, String className, String fieldName) throws OgnlException { Exception reason; try { final Class c = classForName(context, className); /* * Check for virtual static field "class"; this cannot interfere with normal static * fields because it is a reserved word. */ if (fieldName.equals("class")) { return c; } else if (c.isEnum()) { try { return Enum.valueOf((Class) c, fieldName); } catch (IllegalArgumentException e) { // ignore it, try static field } } final Field f = getField(c, fieldName); if (f == null) { throw new NoSuchFieldException(fieldName); } if (!Modifier.isStatic(f.getModifiers())) { throw new OgnlException("Field " + fieldName + " of class " + className + " is not static"); } Object result; if (isAccessible(context, null, f, null)) { final Object state = context.getMemberAccess().setup(context, null, f, null); try { result = f.get(null); } finally { context.getMemberAccess().restore(context, null, f, null, state); } } else { throw new IllegalAccessException("Access to " + fieldName + " of class " + className + " is forbidden"); } return result; } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | IllegalAccessException e) { reason = e; } throw new OgnlException("Could not get static field " + fieldName + " from class " + className, reason); } public static List getDeclaredMethods(Class targetClass, String propertyName, boolean findSets) { String baseName = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1); List methods = new ArrayList<>(); List methodNames = new ArrayList<>(2); if (findSets) { methodNames.add(SET_PREFIX + baseName); } else { methodNames.add(IS_PREFIX + baseName); methodNames.add(GET_PREFIX + baseName); } for (String methodName : methodNames) { DeclaredMethodCacheEntry key = new DeclaredMethodCacheEntry(targetClass); List methodList = cache.getMethod(key).get(methodName); if (methodList != null) { methods.addAll(methodList); } } return methods; } /** * Convenience used to check if a method is a synthetic method so as to avoid * calling un-callable methods. These methods are not considered callable by * OGNL in almost all circumstances. *

* This method considers any synthetic method (even bridge methods) as being un-callable. * Even though synthetic and bridge methods can technically be called, by default * OGNL excludes them from consideration. *

* Synthetic methods should be excluded in general, since calling such methods * could introduce unanticipated risks. * * @param m The method to check. * @return True if the method should be callable (non-synthetic), false otherwise. */ public static boolean isMethodCallable(Method m) { return !(m.isSynthetic() || m.isBridge()); } /** * Convenience used to check if a method is either a non-synthetic method or * a bridge method. *

* Warning: This method should NOT be used as a direct replacement for * {@link #isMethodCallable(Method)}. Almost all OGNL processing assumes the * exclusion of synthetic methods in order to process correctly. Only * use this method to determine method callability for any OGNL processing * after careful consideration. *

* This method considers synthetic methods that are not also bridge methods * as being un-callable. *

* Synthetic methods should be excluded in general, since calling such methods * could introduce unanticipated risks. * * @param m The method to check. * @return True if the method should be callable (non-synthetic or bridge), false otherwise. * @since 3.2.16 */ static boolean isMethodCallable_BridgeOrNonSynthetic(Method m) { return !m.isSynthetic() || m.isBridge(); // Reference: See PR#104. } /** * cache get methods * * @param targetClass the Class to invoke the property name "getter" retrieval on. * @param propertyName the name of the property for which a "getter" is sought. * @return the Method representing a "getter" for propertyName of targetClass. */ public static Method getGetMethod(Class targetClass, String propertyName) { // Cache is a map in two levels, so we provide two keys (see comments in ClassPropertyMethodCache below) Method method = cacheGetMethod.get(targetClass, propertyName); if (method == ClassPropertyMethodCache.NULL_REPLACEMENT) { return null; } if (method != null) return method; method = _getGetMethod(targetClass, propertyName); // will be null if not found - will cache it anyway cacheGetMethod.put(targetClass, propertyName, method); return method; } /** * Returns a qualifying get (getter) method, if one is available for the given targetClass and propertyName. *

* Note: From OGNL 3.1.25 onward, this method will attempt to find the first get getter method(s) that match: * 1) First get (getter) method, whether public or not. * 2) First public get (getter) method, provided the method's declaring class is also public. * This may be the same as 1), if 1) is also public and its declaring class is also public. * 3) First public non-Default interface get (getter) method, provided the method's declaring class is also public. * The order of preference (priority) for the above matches will be 2 (1st public getter), * 3 (1st public non-Default interface getter), 1 (1st getter of any kind). * This updated methodology should help limit the need to modify method accessibility levels in some circumstances. * * @param targetClass Class to search for a get method (getter). * @param propertyName Name of the property for the get method (getter). */ private static Method _getGetMethod(Class targetClass, String propertyName) { Method result; List methods = getDeclaredMethods(targetClass, propertyName, false /* find 'get' methods */); Method firstGetter = null; Method firstPublicGetter = null; Method firstNonDefaultPublicInterfaceGetter = null; for (Method method : methods) { Class[] mParameterTypes = findParameterTypes(targetClass, method); //getParameterTypes(m); if (mParameterTypes.length == 0) { boolean declaringClassIsPublic = Modifier.isPublic(method.getDeclaringClass().getModifiers()); if (firstGetter == null) { firstGetter = method; if (_useFirstMatchGetSetLookup) { break; // Stop looking (emulate original logic, return 1st match) } } if (Modifier.isPublic(method.getModifiers()) && declaringClassIsPublic) { firstPublicGetter = method; break; // Stop looking (this is the best possible match) } if (firstNonDefaultPublicInterfaceGetter == null && isNonDefaultPublicInterfaceMethod(method) && declaringClassIsPublic) { firstNonDefaultPublicInterfaceGetter = method; } } } result = (firstPublicGetter != null) ? firstPublicGetter : (firstNonDefaultPublicInterfaceGetter != null) ? firstNonDefaultPublicInterfaceGetter : firstGetter; return result; } public static > boolean isMethodAccessible(C context, Object target, Method method, String propertyName) { return (method != null) && isAccessible(context, target, method, propertyName); } public static > boolean hasGetMethod(C context, Object target, Class targetClass, String propertyName) { return isMethodAccessible(context, target, getGetMethod(targetClass, propertyName), propertyName); } /** * cache set methods method * * @param context the current execution context. * @param targetClass the Class to invoke the property name "setter" retrieval on. * @param propertyName the name of the property for which a "setter" is sought. * @return the Method representing a "setter" for propertyName of targetClass. */ public static > Method getSetMethod(C context, Class targetClass, String propertyName) { // Cache is a map in two levels, so we provide two keys (see comments in ClassPropertyMethodCache below) Method method = cacheSetMethod.get(targetClass, propertyName); if (method == ClassPropertyMethodCache.NULL_REPLACEMENT) { return null; } if (method != null) return method; // By checking key existence now and not before calling 'get', we will save a map resolution 90% of the times // if (cacheSetMethod.containsKey(targetClass, propertyName)) // return null; method = _getSetMethod(context, targetClass, propertyName); // will be null if not found - will cache it anyway cacheSetMethod.put(targetClass, propertyName, method); return method; } /** * Returns a qualifying set (setter) method, if one is available for the given targetClass and propertyName. *

* Note: From OGNL 3.1.25 onward, this method will attempt to find the first set setter method(s) that match: * 1) First set (setter) method, whether public or not. * 2) First public set (setter) method, provided the method's declaring class is also public. * This may be the same as 1), if 1) is also public and its declaring class is also public. * 3) First public non-Default interface set (setter) method, provided the method's declaring class is also public. * The order of preference (priority) for the above matches will be 2 (1st public setter), * 3 (1st public non-Default interface setter), 1 (1st setter of any kind). * This updated methodology should help limit the need to modify method accessibility levels in some circumstances. * * @param context The current execution context. * @param targetClass Class to search for a set method (setter). * @param propertyName Name of the property for the set method (setter). */ private static Method _getSetMethod(OgnlContext context, Class targetClass, String propertyName) { Method result; List methods = getDeclaredMethods(targetClass, propertyName, true /* find 'set' methods */); Method firstSetter = null; Method firstPublicSetter = null; Method firstNonDefaultPublicInterfaceSetter = null; for (Method method : methods) { Class[] mParameterTypes = findParameterTypes(targetClass, method); //getParameterTypes(m); if (mParameterTypes.length == 1) { boolean declaringClassIsPublic = Modifier.isPublic(method.getDeclaringClass().getModifiers()); if (firstSetter == null) { firstSetter = method; if (_useFirstMatchGetSetLookup) { break; // Stop looking (emulate original logic, return 1st match) } } if (Modifier.isPublic(method.getModifiers()) && declaringClassIsPublic) { firstPublicSetter = method; break; // Stop looking (this is the best possible match) } if (firstNonDefaultPublicInterfaceSetter == null && isNonDefaultPublicInterfaceMethod(method) && declaringClassIsPublic) { firstNonDefaultPublicInterfaceSetter = method; } } } result = (firstPublicSetter != null) ? firstPublicSetter : (firstNonDefaultPublicInterfaceSetter != null) ? firstNonDefaultPublicInterfaceSetter : firstSetter; return result; } public static boolean hasSetMethod(OgnlContext context, Object target, Class targetClass, String propertyName) { return isMethodAccessible(context, target, getSetMethod(context, targetClass, propertyName), propertyName); } public static boolean hasGetProperty(OgnlContext context, Object target, Object oname) throws IntrospectionException { Class targetClass = (target == null) ? null : target.getClass(); String name = oname.toString(); return hasGetMethod(context, target, targetClass, name) || hasField(context, target, targetClass, name); } public static boolean hasSetProperty(OgnlContext context, Object target, Object oname) throws IntrospectionException { Class targetClass = (target == null) ? null : target.getClass(); String name = oname.toString(); return hasSetMethod(context, target, targetClass, name) || hasField(context, target, targetClass, name); } /** * This method returns the property descriptors for the given class as a Map. * * @param targetClass The class to get the descriptors for. * @return Map of property descriptors for class. */ public static Map getPropertyDescriptors(Class targetClass) { return cache.getPropertyDescriptor(targetClass); } /** * This method returns a PropertyDescriptor for the given class and property name using a Map * lookup (using getPropertyDescriptorsMap()). * * @param targetClass the class to get the descriptors for. * @param propertyName the property name of targetClass for which a Descriptor is requested. * @return the PropertyDescriptor for propertyName of targetClass. * @throws OgnlException On general errors. */ public static PropertyDescriptor getPropertyDescriptor(Class targetClass, String propertyName) throws OgnlException { if (targetClass == null) return null; return getPropertyDescriptors(targetClass).get(propertyName); } public static PropertyDescriptor[] getPropertyDescriptorsArray(Class targetClass) { Collection propertyDescriptors = getPropertyDescriptors(targetClass).values(); return propertyDescriptors.toArray(new PropertyDescriptor[0]); } /** * Gets the property descriptor with the given name for the target class given. * * @param targetClass Class for which property descriptor is desired * @param name Name of property * @return PropertyDescriptor of the named property or null if the class has no property with * the given name */ public static PropertyDescriptor getPropertyDescriptorFromArray(Class targetClass, String name) { PropertyDescriptor result = null; PropertyDescriptor[] propertyDescriptors = getPropertyDescriptorsArray(targetClass); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (result != null) { break; } if (propertyDescriptor.getName().compareTo(name) == 0) { result = propertyDescriptor; } } return result; } public static void setMethodAccessor(Class clazz, MethodAccessor accessor) { cache.setMethodAccessor(clazz, accessor); } public static > MethodAccessor getMethodAccessor(Class clazz) throws OgnlException { return cache.getMethodAccessor(clazz); } public static > void setPropertyAccessor(Class clazz, PropertyAccessor accessor) { cache.setPropertyAccessor(clazz, accessor); } public static > PropertyAccessor getPropertyAccessor(Class clazz) throws OgnlException { return cache.getPropertyAccessor(clazz); } public static ElementsAccessor getElementsAccessor(Class clazz) throws OgnlException { return cache.getElementsAccessor(clazz); } public static void setElementsAccessor(Class clazz, ElementsAccessor accessor) { cache.setElementsAccessor(clazz, accessor); } public static > NullHandler getNullHandler(Class clazz) throws OgnlException { return cache.getNullHandler(clazz); } public static void setNullHandler(Class clazz, NullHandler handler) { cache.setNullHandler(clazz, handler); } public static > Object getProperty(C context, Object source, Object name) throws OgnlException { PropertyAccessor accessor; if (source == null) { throw new OgnlException("source is null for getProperty(null, \"" + name + "\")"); } if ((accessor = getPropertyAccessor(getTargetClass(source))) == null) { throw new OgnlException("No property accessor for " + getTargetClass(source).getName()); } return accessor.getProperty(context, source, name); } public static > void setProperty(C context, Object target, Object name, Object value) throws OgnlException { PropertyAccessor accessor; if (target == null) { throw new OgnlException("target is null for setProperty(null, \"" + name + "\", " + value + ")"); } if ((accessor = getPropertyAccessor(getTargetClass(target))) == null) { throw new OgnlException("No property accessor for " + getTargetClass(target).getName()); } accessor.setProperty(context, target, name, value); } /** * Determines the index property type, if any. Returns INDEXED_PROPERTY_NONE if * the property is not index-accessible as determined by OGNL or JavaBeans. If it is indexable * then this will return whether it is a JavaBeans indexed property, conforming to the indexed * property patterns (returns INDEXED_PROPERTY_INT) or if it conforms to the * OGNL arbitrary object indexable (returns INDEXED_PROPERTY_OBJECT). * * @param sourceClass the Class to invoke indexed property type retrieval on. * @param name the name of the property for which an indexed property type is sought. * @return the indexed property type (int) for the property name of sourceClass. Returns INDEXED_PROPERTY_NONE if name is not an indexed property. * @throws OgnlException for lots of different reasons. */ public static int getIndexedPropertyType(Class sourceClass, String name) throws OgnlException { int result = INDEXED_PROPERTY_NONE; try { PropertyDescriptor pd = getPropertyDescriptor(sourceClass, name); if (pd != null) { if (pd instanceof IndexedPropertyDescriptor) { result = INDEXED_PROPERTY_INT; } else { if (pd instanceof ObjectIndexedPropertyDescriptor) { result = INDEXED_PROPERTY_OBJECT; } } } } catch (Exception ex) { throw new OgnlException("problem determining if '" + name + "' is an indexed property", ex); } return result; } public static > Object getIndexedProperty(C context, Object source, String name, Object index) throws OgnlException { Object[] args = new Object[]{index}; try { PropertyDescriptor pd = getPropertyDescriptor((source == null) ? null : source.getClass(), name); Method m; if (pd instanceof IndexedPropertyDescriptor ipd) { m = ipd.getIndexedReadMethod(); } else { if (pd instanceof ObjectIndexedPropertyDescriptor oipd) { m = oipd.getIndexedReadMethod(); } else { throw new OgnlException("property '" + name + "' is not an indexed property"); } } return callMethod(context, source, m.getName(), args); } catch (OgnlException ex) { throw ex; } catch (Exception ex) { throw new OgnlException("getting indexed property descriptor for '" + name + "'", ex); } } public static > void setIndexedProperty(C context, Object source, String name, Object index, Object value) throws OgnlException { Object[] args = new Object[]{index, value}; try { PropertyDescriptor pd = getPropertyDescriptor((source == null) ? null : source.getClass(), name); Method m; if (pd instanceof IndexedPropertyDescriptor ipd) { m = ipd.getIndexedWriteMethod(); } else { if (pd instanceof ObjectIndexedPropertyDescriptor oipd) { m = oipd.getIndexedWriteMethod(); } else { throw new OgnlException("property '" + name + "' is not an indexed property"); } } callMethod(context, source, m.getName(), args); } catch (OgnlException ex) { throw ex; } catch (Exception ex) { throw new OgnlException("getting indexed property descriptor for '" + name + "'", ex); } } public static > EvaluationPool getEvaluationPool() { return _evaluationPool; } /** * Registers the specified {@link ClassCacheInspector} with all class reflection based internal * caches. This may have a significant performance impact so be careful using this in production scenarios. * * @param inspector The inspector instance that will be registered with all internal cache instances. */ public static void setClassCacheInspector(ClassCacheInspector inspector) { cache.setClassCacheInspector(inspector); } public static > Method getMethod(C context, Class target, String name, Node[] children, boolean includeStatic) throws Exception { Class[] parms; if (children != null && children.length > 0) { parms = new Class[children.length]; // used to reset context after loop Class currType = context.getCurrentType(); Class currAccessor = context.getCurrentAccessor(); Object cast = context.get(ExpressionCompiler.PRE_CAST); context.setCurrentObject(context.getRoot()); context.setCurrentType(context.getRoot() != null ? context.getRoot().getClass() : null); context.setCurrentAccessor(null); context.setPreviousType(null); for (int i = 0; i < children.length; i++) { children[i].toGetSourceString(context, context.getRoot()); parms[i] = context.getCurrentType(); } context.put(ExpressionCompiler.PRE_CAST, cast); context.setCurrentType(currType); context.setCurrentAccessor(currAccessor); context.setCurrentObject(target); } else { parms = EMPTY_CLASS_ARRAY; } List methods = OgnlRuntime.getMethods(target, name, includeStatic); if (methods == null) return null; for (Method method : methods) { boolean varArgs = method.isVarArgs(); if (parms.length != method.getParameterTypes().length && !varArgs) continue; Class[] mparms = method.getParameterTypes(); boolean matched = true; for (int p = 0; p < mparms.length; p++) { if (varArgs && mparms[p].isArray()) { continue; } if (parms[p] == null) { matched = false; break; } if (parms[p] == mparms[p]) continue; if (mparms[p].isPrimitive() && Character.TYPE != mparms[p] && Byte.TYPE != mparms[p] && Number.class.isAssignableFrom(parms[p]) && OgnlRuntime.getPrimitiveWrapperClass(parms[p]) == mparms[p]) { continue; } matched = false; break; } if (matched) return method; } return null; } /** * Finds the best possible match for a method on the specified target class with a matching * name. * *

* The name matched will also try different combinations like is + name, has + name, get + name, etc.. *

* * @param target The class to find a matching method against. * @param name The name of the method. * @return The most likely matching {@link Method}, or null if none could be found. */ public static Method getReadMethod(Class target, String name) { return getReadMethod(target, name, null); } public static Method getReadMethod(Class target, String name, Class[] argClasses) { try { if (name.indexOf('"') >= 0) name = name.replaceAll("\"", ""); name = name.toLowerCase(); Method[] methods = target.getMethods(); // exact matches first ArrayList candidates = new ArrayList<>(); for (Method method : methods) { // Consider bridge methods as callable (also) for Read methods. if (!isMethodCallable_BridgeOrNonSynthetic(method)) { continue; } if ((method.getName().equalsIgnoreCase(name) || method.getName().toLowerCase().equals("get" + name) || method.getName().toLowerCase().equals("has" + name) || method.getName().toLowerCase().equals("is" + name)) && !method.getName().startsWith("set")) { candidates.add(method); } } if (!candidates.isEmpty()) { MatchingMethod mm = findBestMethod(candidates, target, name, argClasses); if (mm != null) return mm.mMethod; } for (Method method : methods) { // Consider bridge methods as callable (also) for Read methods. if (!isMethodCallable_BridgeOrNonSynthetic(method)) { continue; } if (method.getName().equalsIgnoreCase(name) && !method.getName().startsWith("set") && !method.getName().startsWith("get") && !method.getName().startsWith("is") && !method.getName().startsWith("has") && method.getReturnType() != Void.TYPE) { if (!candidates.contains(method)) { candidates.add(method); } } } if (!candidates.isEmpty()) { MatchingMethod mm = findBestMethod(candidates, target, name, argClasses); if (mm != null) return mm.mMethod; } // try one last time adding a get to beginning if (!name.startsWith("get")) { Method ret = OgnlRuntime.getReadMethod(target, "get" + name, argClasses); if (ret != null) return ret; } if (!candidates.isEmpty()) { // we need to do conversions. // TODO we have to find out which conversions are possible! int reqArgCount = argClasses == null ? 0 : argClasses.length; for (Method m : candidates) { if (m.getParameterTypes().length == reqArgCount) return m; } } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return null; } public static Method getWriteMethod(Class target, String name) { return getWriteMethod(target, name, null); } public static Method getWriteMethod(Class target, String name, Class[] argClasses) { try { if (name.indexOf('"') >= 0) { name = name.replaceAll("\"", ""); } BeanInfo info = Introspector.getBeanInfo(target); MethodDescriptor[] methods = info.getMethodDescriptors(); ArrayList candidates = new ArrayList<>(); for (MethodDescriptor method : methods) { // Consider bridge methods as callable (also) for Write methods. if (!isMethodCallable_BridgeOrNonSynthetic(method.getMethod())) { continue; } if ((method.getName().equalsIgnoreCase(name) || method.getName().toLowerCase().equals("set" + name.toLowerCase())) && !method.getName().startsWith("get")) { candidates.add(method.getMethod()); } } if (!candidates.isEmpty()) { MatchingMethod mm = findBestMethod(candidates, target, name, argClasses); if (mm != null) return mm.mMethod; } // try again on pure class Method[] cmethods = target.getMethods(); for (Method cmethod : cmethods) { // Consider bridge methods as callable (also) for Write methods. if (!isMethodCallable_BridgeOrNonSynthetic(cmethod)) { continue; } if ((cmethod.getName().equalsIgnoreCase(name) || cmethod.getName().toLowerCase().equals("set" + name.toLowerCase())) && !cmethod.getName().startsWith("get")) { if (!candidates.contains(cmethod)) candidates.add(cmethod); } } if (!candidates.isEmpty()) { MatchingMethod mm = findBestMethod(candidates, target, name, argClasses); if (mm != null) return mm.mMethod; } // try one last time adding a set to beginning if (!name.startsWith("set")) { Method ret = OgnlRuntime.getReadMethod(target, "set" + name, argClasses); if (ret != null) return ret; } if (!candidates.isEmpty()) { // we need to do conversions. // TODO we have to find out which conversions are possible! int reqArgCount = argClasses == null ? 0 : argClasses.length; for (Method m : candidates) { if (m.getParameterTypes().length == reqArgCount) return m; } if (argClasses == null && candidates.size() == 1) { // this seems to be the TestCase TestOgnlRuntime.test_Complicated_Inheritance() - is this a real world use case? return candidates.get(0); } } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return null; } public static PropertyDescriptor getProperty(Class target, String name) { try { BeanInfo info = Introspector.getBeanInfo(target); PropertyDescriptor[] pds = info.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { if (pd.getName().equalsIgnoreCase(name) || pd.getName().toLowerCase().endsWith(name.toLowerCase())) return pd; } } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } return null; } public static boolean isBoolean(String expression) { if (expression == null) return false; return "true".equals(expression) || "false".equals(expression) || "!true".equals(expression) || "!false".equals(expression) || "(true)".equals(expression) || "!(true)".equals(expression) || "(false)".equals(expression) || "!(false)".equals(expression) || expression.startsWith("ognl.OgnlOps"); } /** * Compares the {@link OgnlContext#getCurrentType()} and {@link OgnlContext#getPreviousType()} class types * on the stack to determine if a numeric expression should force object conversion. *

* Normally used in conjunction with the forceConversion parameter of * {@link OgnlRuntime#getChildSource(OgnlContext, Object, Node)}. *

* * @param context The current context. * @return True, if the class types on the stack wouldn't be comparable in a pure numeric expression such as o1 >= o2. */ public static > boolean shouldConvertNumericTypes(C context) { if (context.getCurrentType() == null || context.getPreviousType() == null) return true; if (context.getCurrentType() == context.getPreviousType() && context.getCurrentType().isPrimitive() && context.getPreviousType().isPrimitive()) return false; return context.getCurrentType() != null && !context.getCurrentType().isArray() && context.getPreviousType() != null && !context.getPreviousType().isArray(); } /** * Attempts to get the java source string represented by the specific child expression * via the {@link JavaSource#toGetSourceString(OgnlContext, Object)} interface method. * * @param context The ognl context to pass to the child. * @param target The current object target to use. * @param child The child expression. * @return The result of calling {@link JavaSource#toGetSourceString(OgnlContext, Object)} plus additional * enclosures of {@link OgnlOps#convertValue(Object, Class, boolean)} for conversions. */ public static > String getChildSource(C context, Object target, Node child) { String pre = (String) context.get("_currentChain"); if (pre == null) pre = ""; try { child.getValue(context, target); } catch (NullPointerException e) { // ignore } catch (ArithmeticException e) { context.setCurrentType(int.class); return "0"; } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } String source; try { source = child.toGetSourceString(context, target); } catch (Throwable t) { throw OgnlOps.castToRuntime(t); } // handle root / method expressions that may not have proper root java source access if (!(child instanceof ASTConst) && (target == null || context.getRoot() != target)) { source = pre + source; } if (context.getRoot() != null) { source = ExpressionCompiler.getRootExpression(child, context.getRoot(), context) + source; context.setCurrentAccessor(context.getRoot().getClass()); } if (child instanceof ASTChain) { String cast = (String) context.remove(ExpressionCompiler.PRE_CAST); if (cast == null) cast = ""; source = cast + source; } if (source == null || source.trim().isEmpty()) source = "null"; return source; } /* * The idea behind this class is to provide a very fast way to cache getter/setter methods indexed by their class * and property name. * * Instead of creating any kind of complex key object (or a String key by appending class name and property), this * class directly uses the Class clazz and the String propertyName as keys of two levels of ConcurrentHashMaps, * so that it takes advantage of the fact that these two classes are immutable and that their respective hashCode() * and equals() methods are extremely fast and optimized. These two aspects should improve Map access performance. * * Also, using these structure instead of any other kind of key on a single-level map should save a lot of memory * given no specialized cache objects (be them of a specific CacheKey class or mere Strings) ever have to be created * for simply accessing the cache in search for a getter/setter method. * */ private static final class ClassPropertyMethodCache { // ConcurrentHashMaps do not allow null keys or values, so we will use one of this class's own methods as // a replacement for signaling when the true cached value is 'null' private static final Method NULL_REPLACEMENT; private final ConcurrentHashMap, ConcurrentHashMap> cache = new ConcurrentHashMap<>(); static { try { NULL_REPLACEMENT = ClassPropertyMethodCache.class.getDeclaredMethod("get", Class.class, String.class); } catch (NoSuchMethodException e) { throw new RuntimeException(e); // Will never happen, it's our own method, we know it exists } } ClassPropertyMethodCache() { super(); } Method get(Class clazz, String propertyName) { ConcurrentHashMap methodsByPropertyName = this.cache.get(clazz); if (methodsByPropertyName == null) { return null; } return methodsByPropertyName.get(propertyName); } void put(Class clazz, String propertyName, Method method) { ConcurrentHashMap methodsByPropertyName = this.cache.get(clazz); if (methodsByPropertyName == null) { methodsByPropertyName = new ConcurrentHashMap<>(); ConcurrentHashMap old = this.cache.putIfAbsent(clazz, methodsByPropertyName); if (null != old) { methodsByPropertyName = old; } } methodsByPropertyName.putIfAbsent(propertyName, (method == null ? NULL_REPLACEMENT : method)); } /** * Allow clearing for the underlying cache of the ClassPropertyMethodCache. * * @since 3.1.25 */ void clear() { this.cache.clear(); } } /** * Detect the (reported) Major Java version running OGNL. *

* Should support naming conventions of pre-JDK9 and JDK9+. * See JEP 223: New Version-String Scheme for details. * * @return Detected Major Java Version, or 17 (minimum supported version for OGNL) if unable to detect. * @since 3.1.25 */ static int detectMajorJavaVersion() { int majorVersion = -1; try { majorVersion = parseMajorJavaVersion(System.getProperty("java.version")); } catch (Exception ex) { // Unavailable (SecurityException, etc.) } if (majorVersion == -1) { majorVersion = 17; // Return minimum supported Java version for OGNL } return majorVersion; } /** * Parse a Java version string to determine the Major Java version. *

* Should support naming conventions of pre-JDK9 and JDK9+. * See JEP 223: New Version-String Scheme for details. * * @return Detected Major Java Version, or 17 (minimum supported version for OGNL) if unable to detect. * @since 3.1.25 */ static int parseMajorJavaVersion(String versionString) { int majorVersion = -1; try { if (versionString != null && !versionString.isEmpty()) { final String[] sections = versionString.split("[.\\-+]"); final int firstSection; final int secondSection; if (sections.length > 0) { // Should not happen, guard anyway if (!sections[0].isEmpty()) { if (sections.length > 1 && !sections[1].isEmpty()) { firstSection = Integer.parseInt(sections[0]); if (sections[1].matches("\\d+")) { secondSection = Integer.parseInt(sections[1]); } else { secondSection = -1; } } else { firstSection = Integer.parseInt(sections[0]); secondSection = -1; } if (firstSection == 1 && secondSection != -1) { majorVersion = secondSection; // Pre-JDK 9 versioning } else { majorVersion = firstSection; // JDK9+ versioning } } } } } catch (Exception ex) { // Unavailable (NumberFormatException, etc.) } if (majorVersion == -1) { majorVersion = 17; // Return minimum supported Java version for OGNL } return majorVersion; } /** * Returns the value of the flag indicating whether "stricter" invocation is * in effect or not. *

* Note: Value is controlled by a Java option flag {@link OgnlRuntime#USE_STRICTER_INVOCATION}. * * @return true if stricter invocation is in effect, false otherwise. * @since 3.1.25 */ public static boolean getUseStricterInvocationValue() { return _useStricterInvocation; } /** * Returns the value of the flag indicating whether the old "first match" lookup for * getters/setters is in effect or not. *

* Note: Value is controlled by a Java option flag {@link OgnlRuntime#USE_FIRSTMATCH_GETSET_LOOKUP}. * * @return true if the old "first match" lookup is in effect, false otherwise. * @since 3.1.25 */ public static boolean getUseFirstMatchGetSetLookupValue() { return _useFirstMatchGetSetLookup; } /** * Returns true if the given member is accessible or can be made accessible * by this object. * * @param context the current execution context. * @param target the Object to test accessibility for. * @param member the Member to test accessibility for. * @param propertyName the property to test accessibility for. * @return true if the target/member/propertyName is accessible in the context, false otherwise. */ private static > boolean isAccessible(C context, Object target, Member member, String propertyName) { return context.getMemberAccess().isAccessible(context, target, member, propertyName); } } ================================================ FILE: ognl/src/main/java/ognl/PrimitiveDefaults.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; class PrimitiveDefaults { private final Map, Object> PRIMITIVE_DEFAULTS = new HashMap, Object>(13); PrimitiveDefaults() { PRIMITIVE_DEFAULTS.put(Boolean.TYPE, Boolean.FALSE); PRIMITIVE_DEFAULTS.put(Boolean.class, Boolean.FALSE); PRIMITIVE_DEFAULTS.put(Byte.TYPE, (byte) 0); PRIMITIVE_DEFAULTS.put(Byte.class, (byte) 0); PRIMITIVE_DEFAULTS.put(Short.TYPE, (short) 0); PRIMITIVE_DEFAULTS.put(Short.class, (short) 0); PRIMITIVE_DEFAULTS.put(Character.TYPE, (char) 0); PRIMITIVE_DEFAULTS.put(Integer.TYPE, 0); PRIMITIVE_DEFAULTS.put(Long.TYPE, 0L); PRIMITIVE_DEFAULTS.put(Float.TYPE, 0.0f); PRIMITIVE_DEFAULTS.put(Double.TYPE, 0.0); PRIMITIVE_DEFAULTS.put(BigInteger.class, BigInteger.ZERO); PRIMITIVE_DEFAULTS.put(BigDecimal.class, BigDecimal.ZERO); } Object get(Class cls) { return PRIMITIVE_DEFAULTS.get(cls); } } ================================================ FILE: ognl/src/main/java/ognl/PrimitiveTypes.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.HashMap; import java.util.Map; class PrimitiveTypes { private final Map> PRIMITIVE_TYPES = new HashMap<>(8); PrimitiveTypes() { PRIMITIVE_TYPES.put("boolean", Boolean.TYPE); PRIMITIVE_TYPES.put("byte", Byte.TYPE); PRIMITIVE_TYPES.put("short", Short.TYPE); PRIMITIVE_TYPES.put("char", Character.TYPE); PRIMITIVE_TYPES.put("int", Integer.TYPE); PRIMITIVE_TYPES.put("long", Long.TYPE); PRIMITIVE_TYPES.put("float", Float.TYPE); PRIMITIVE_TYPES.put("double", Double.TYPE); } Class get(String className) { return PRIMITIVE_TYPES.get(className); } } ================================================ FILE: ognl/src/main/java/ognl/PrimitiveWrapperClasses.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.IdentityHashMap; import java.util.Map; /** * Used to provide primitive type equivalent conversions into and out of native / object types. */ class PrimitiveWrapperClasses { private final Map, Class> PRIMITIVE_WRAPPER_CLASSES = new IdentityHashMap<>(16); PrimitiveWrapperClasses() { PRIMITIVE_WRAPPER_CLASSES.put(Boolean.TYPE, Boolean.class); PRIMITIVE_WRAPPER_CLASSES.put(Boolean.class, Boolean.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Byte.TYPE, Byte.class); PRIMITIVE_WRAPPER_CLASSES.put(Byte.class, Byte.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Character.TYPE, Character.class); PRIMITIVE_WRAPPER_CLASSES.put(Character.class, Character.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Short.TYPE, Short.class); PRIMITIVE_WRAPPER_CLASSES.put(Short.class, Short.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Integer.TYPE, Integer.class); PRIMITIVE_WRAPPER_CLASSES.put(Integer.class, Integer.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Long.TYPE, Long.class); PRIMITIVE_WRAPPER_CLASSES.put(Long.class, Long.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Float.TYPE, Float.class); PRIMITIVE_WRAPPER_CLASSES.put(Float.class, Float.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Double.TYPE, Double.class); PRIMITIVE_WRAPPER_CLASSES.put(Double.class, Double.TYPE); } Class get(Class cls) { return PRIMITIVE_WRAPPER_CLASSES.get(cls); } } ================================================ FILE: ognl/src/main/java/ognl/PropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; /** * This interface defines methods for setting and getting a property from a target object. A * "property" in this case is a named data value that takes the generic form of an Object---the same * definition as is used by beans. But the operational semantics of the term will vary by * implementation of this interface: a bean-style implementation will get and set properties as * beans do, by reflection on the target object's class, but other implementations are possible, * such as one that uses the property name as a key into a map. *

* An implementation of this interface will often require that its target objects all be of some * particular type. For example, the MapPropertyAccessor class requires that its targets all * implement the java.util.Map interface. *

* Note that the "name" of a property is represented by a generic Object. Some implementations may * require properties' names to be Strings, while others may allow them to be other types---for * example, ArrayPropertyAccessor treats Number names as indexes into the target object, which must * be an array. */ public interface PropertyAccessor> { /** * Extracts and returns the property of the given name from the given target object. * * @param context The current execution context. * @param target the object to get the property from * @param name the name of the property to get. * @return the current value of the given property in the given object * @throws OgnlException if there is an error locating the property in the given object */ Object getProperty(C context, Object target, Object name) throws OgnlException; /** * Sets the value of the property of the given name in the given target object. * * @param context The current execution context. * @param target the object to set the property in * @param name the name of the property to set * @param value the new value for the property. * @throws OgnlException if there is an error setting the property in the given object */ void setProperty(C context, Object target, Object name, Object value) throws OgnlException; /** * Returns a java string representing the textual method that should be called to access a * particular element. (ie "get") * * @param context The current execution context. * @param target The current object target on the expression tree being evaluated. * @param index The index object that will be placed inside the string to access the value. * @return The source accessor method to call. */ String getSourceAccessor(C context, Object target, Object index); /** * Returns a java string representing the textual method that should be called to set a * particular element. (ie "set") * * @param context The current execution context. * @param target The current object target on the expression tree being evaluated. * @param index The index object that will be placed inside the string to set the value. * @return The source setter method to call. */ String getSourceSetter(C context, Object target, Object index); } ================================================ FILE: ognl/src/main/java/ognl/SetPropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.util.Set; /** * Implementation of PropertyAccessor that uses numbers and dynamic subscripts as * properties to index into Lists. */ public class SetPropertyAccessor> extends ObjectPropertyAccessor implements PropertyAccessor { public Object getProperty(C context, Object target, Object name) throws OgnlException { Set set = (Set) target; if (name instanceof String) { Object result; if (name.equals("size")) { result = set.size(); } else { if (name.equals("iterator")) { result = set.iterator(); } else { if (name.equals("isEmpty")) { result = set.isEmpty() ? Boolean.TRUE : Boolean.FALSE; } else { result = super.getProperty(context, target, name); } } } return result; } throw new NoSuchPropertyException(target, name); } } ================================================ FILE: ognl/src/main/java/ognl/SimpleNode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.enhance.ExpressionAccessor; import java.io.PrintWriter; import java.io.Serial; import java.io.Serializable; public abstract class SimpleNode> implements Node, Serializable { @Serial private static final long serialVersionUID = 369358170335048384L; protected Node parent; protected Node[] children; protected int id; protected OgnlParser parser; private boolean constantValueCalculated; private volatile boolean hasConstantValue; private Object constantValue; private ExpressionAccessor expressionAccessor; public SimpleNode(int i) { id = i; } public SimpleNode(OgnlParser p, int i) { this(i); parser = p; } public void jjtOpen() { } public void jjtClose() { } public void jjtSetParent(Node n) { parent = n; } public Node jjtGetParent() { return parent; } public void jjtAddChild(Node n, int i) { if (children == null) { children = new Node[i + 1]; } else if (i >= children.length) { Node[] c = new Node[i + 1]; System.arraycopy(children, 0, c, 0, children.length); children = c; } children[i] = n; } public Node jjtGetChild(int i) { return children[i]; } public int jjtGetNumChildren() { return (children == null) ? 0 : children.length; } /* * You can override these two methods in subclasses of SimpleNode to customize the way the node * appears when the tree is dumped. If your output uses more than one line you should override * toString(String), otherwise overriding toString() is probably all you need to do. */ public String toString() { return OgnlParserTreeConstants.jjtNodeName[id]; } // OGNL additions public String toString(String prefix) { return prefix + OgnlParserTreeConstants.jjtNodeName[id] + " " + this; } public String toGetSourceString(C context, Object target) { return toString(); } public String toSetSourceString(C context, Object target) { return toString(); } /* * Override this method if you want to customize how the node dumps out its children. */ public void dump(PrintWriter writer, String prefix) { writer.println(toString(prefix)); if (children != null) { for (Node child : children) { SimpleNode n = (SimpleNode) child; if (n != null) { n.dump(writer, prefix + " "); } } } } public int getIndexInParent() { int result = -1; if (parent != null) { int icount = parent.jjtGetNumChildren(); for (int i = 0; i < icount; i++) { if (parent.jjtGetChild(i) == this) { result = i; break; } } } return result; } public Node getNextSibling() { Node result = null; int i = getIndexInParent(); if (i >= 0) { int icount = parent.jjtGetNumChildren(); if (i < icount) { result = parent.jjtGetChild(i + 1); } } return result; } protected Object evaluateGetValueBody(C context, Object source) throws OgnlException { context.setCurrentObject(source); context.setCurrentNode(this); if (!constantValueCalculated) { constantValueCalculated = true; boolean constant = isConstant(context); if (constant) { constantValue = getValueBody(context, source); } hasConstantValue = constant; } return hasConstantValue ? constantValue : getValueBody(context, source); } protected void evaluateSetValueBody(C context, Object target, Object value) throws OgnlException { context.setCurrentObject(target); context.setCurrentNode(this); setValueBody(context, target, value); } public final Object getValue(C context, Object source) throws OgnlException { Object result = null; if (context.isTraceEvaluations()) { EvaluationPool pool = OgnlRuntime.getEvaluationPool(); Throwable evalException = null; Evaluation evaluation = pool.create(this, source); context.pushEvaluation(evaluation); try { result = evaluateGetValueBody(context, source); } catch (OgnlException | RuntimeException ex) { evalException = ex; throw ex; } finally { Evaluation eval = context.popEvaluation(); eval.setResult(result); if (evalException != null) { eval.setException(evalException); } } } else { result = evaluateGetValueBody(context, source); } return result; } /** * Subclasses implement this method to do the actual work of extracting the appropriate value from the source object. * * @param context the OgnlContext within which to perform the operation. * @param source the Object from which to get the value body. * @return the value body from the source (as appropriate within the provided context). * @throws OgnlException if the value body get fails. */ protected abstract Object getValueBody(C context, Object source) throws OgnlException; public final void setValue(C context, Object target, Object value) throws OgnlException { if (context.isTraceEvaluations()) { EvaluationPool pool = OgnlRuntime.getEvaluationPool(); Throwable evalException = null; Evaluation evaluation = pool.create(this, target, true); context.pushEvaluation(evaluation); try { evaluateSetValueBody(context, target, value); } catch (OgnlException ex) { evalException = ex; ex.setEvaluation(evaluation); throw ex; } catch (RuntimeException ex) { evalException = ex; throw ex; } finally { Evaluation eval = context.popEvaluation(); if (evalException != null) { eval.setException(evalException); } } } else { evaluateSetValueBody(context, target, value); } } /** * Subclasses implement this method to do the actual work of setting the appropriate value in the target object. The default implementation throws an * InappropriateExpressionException, meaning that it cannot be a set expression. * * @param context the OgnlContext within which to perform the operation. * @param target the Object upon which to set the value body. * @param value the Object representing the value body to apply to the target. * @throws OgnlException if the value body set fails. */ protected void setValueBody(C context, Object target, Object value) throws OgnlException { throw new InappropriateExpressionException(this); } /** * Returns true iff this node is constant without respect to the children. * * @param context the OgnlContext within which to perform the operation. * @return true if this node is a constant, false otherwise. * @throws OgnlException if the check fails. */ public boolean isNodeConstant(C context) throws OgnlException { return false; } public boolean isConstant(C context) throws OgnlException { return isNodeConstant(context); } public boolean isNodeSimpleProperty(C context) throws OgnlException { return false; } public boolean isSimpleProperty(C context) throws OgnlException { return isNodeSimpleProperty(context); } public boolean isSimpleNavigationChain(C context) throws OgnlException { return isSimpleProperty(context); } public boolean isEvalChain(C context) throws OgnlException { if (children == null) { return false; } for (Node child : children) { if (child instanceof SimpleNode) { if (((SimpleNode) child).isEvalChain(context)) { return true; } } } return false; } public boolean isSequence(C context) throws OgnlException { if (children == null) { return false; } for (Node child : children) { if (child instanceof SimpleNode) { if (((SimpleNode) child).isSequence(context)) { return true; } } } return false; } public boolean isOperation(C context) throws OgnlException { if (children == null) { return false; } for (Node child : children) { if (child instanceof SimpleNode) { if (((SimpleNode) child).isOperation(context)) { return true; } } } return false; } public boolean isChain(C context) throws OgnlException { if (children == null) { return false; } for (Node child : children) { if (child instanceof SimpleNode) { if (((SimpleNode) child).isChain(context)) { return true; } } } return false; } public boolean isSimpleMethod(C context) throws OgnlException { return false; } protected boolean lastChild(C context) { return parent == null || context.get("_lastChild") != null; } /** * This method may be called from subclasses' jjtClose methods. It flattens the tree under this node by eliminating any children that are of the same class as this * node and copying their children to this node. */ protected void flattenTree() { boolean shouldFlatten = false; int newSize = 0; for (Node child : children) if (child.getClass() == getClass()) { shouldFlatten = true; newSize += child.jjtGetNumChildren(); } else ++newSize; if (shouldFlatten) { Node[] newChildren = new Node[newSize]; int j = 0; for (Node c : children) { if (c.getClass() == getClass()) { for (int k = 0; k < c.jjtGetNumChildren(); ++k) { newChildren[j++] = c.jjtGetChild(k); } } else { newChildren[j++] = c; } } if (j != newSize) throw new Error("Assertion error: " + j + " != " + newSize); children = newChildren; } } public ExpressionAccessor getAccessor() { return expressionAccessor; } public void setAccessor(ExpressionAccessor accessor) { expressionAccessor = accessor; } } ================================================ FILE: ognl/src/main/java/ognl/TypeConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.Member; /** * Interface for accessing the type conversion facilities within a context. */ public interface TypeConverter> { /** * Converts the given value to a given type. The OGNL context, target, member and * name of property being set are given. This method should be able to handle * conversion in general without any context, target, member or property name specified. * * @param context OGNL context under which the conversion is being done * @param target target object in which the property is being set * @param member member (Constructor, Method or Field) being set * @param propertyName property name being set * @param value value to be converted * @param toType type to which value is converted * @return Converted value of type toType or TypeConverter.NoConversionPossible to indicate that the * conversion was not possible. */ Object convertValue(C context, Object target, Member member, String propertyName, Object value, Class toType); } ================================================ FILE: ognl/src/main/java/ognl/enhance/ContextClassLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; import ognl.OgnlContext; public class ContextClassLoader> extends ClassLoader { private final C context; public ContextClassLoader(ClassLoader parentClassLoader, C context) { super(parentClassLoader); this.context = context; } protected Class findClass(String name) throws ClassNotFoundException { if ((context != null) && (context.getClassResolver() != null)) { return context.getClassResolver().classForName(name, context); } return super.findClass(name); } } ================================================ FILE: ognl/src/main/java/ognl/enhance/EnhancedClassLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; public class EnhancedClassLoader extends ClassLoader { public EnhancedClassLoader(ClassLoader parentClassLoader) { super(parentClassLoader); } public Class defineClass(String enhancedClassName, byte[] byteCode) { return defineClass(enhancedClassName, byteCode, 0, byteCode.length); } } ================================================ FILE: ognl/src/main/java/ognl/enhance/ExpressionAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; import ognl.Node; import ognl.OgnlContext; /** * Provides pure java expression paths to get/set values from an ognl expression. This * is achieved by taking an existing {@link Node} parsed expression and using bytecode * enhancements to do the same work using pure java vs the ognl interpreter. */ public interface ExpressionAccessor> { /** * Gets the value represented by this expression path, if any. * * @param context The standard ognl context used for variable substitution/etc. * @param target The root object this expression is meant for. * @return The evaluated value, if any. */ Object get(C context, Object target); /** * Sets the value represented by this expression path, if possible. * * @param context The standard ognl context used for variable substitution/etc. * @param target The root object this expression is meant for. * @param value The new value to set if this expression references a settable property. */ void set(C context, Object target, Object value); /** * Used to set the original root expression node on instances where the compiled version * has to fall back to interpreted syntax because of compilation failures. * * @param expression The root expression node used to generate this accessor. */ void setExpression(Node expression); } ================================================ FILE: ognl/src/main/java/ognl/enhance/ExpressionCompiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewConstructor; import javassist.CtNewMethod; import javassist.LoaderClassPath; import javassist.NotFoundException; import ognl.ASTAnd; import ognl.ASTChain; import ognl.ASTConst; import ognl.ASTCtor; import ognl.ASTList; import ognl.ASTMethod; import ognl.ASTOr; import ognl.ASTProperty; import ognl.ASTRootVarRef; import ognl.ASTStaticField; import ognl.ASTStaticMethod; import ognl.ASTVarRef; import ognl.ClassResolver; import ognl.ExpressionNode; import ognl.Node; import ognl.OgnlContext; import ognl.OgnlRuntime; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Responsible for managing/providing functionality related to compiling generated java source * expressions via bytecode enhancements for a given ognl expression. */ public class ExpressionCompiler> implements OgnlExpressionCompiler { /** * Key used to store any java source string casting statements in the {@link OgnlContext} during * class compilation. */ public static final String PRE_CAST = "_preCast"; /** * {@link ClassLoader} instances. */ protected Map, EnhancedClassLoader> loaders = new HashMap<>(); /** * Javassist class definition pool. */ protected ClassPool classPool; protected int classCounter = 0; /** * Default constructor, does nothing. */ public ExpressionCompiler() { } /** * Used by {@link #castExpression(OgnlContext, Node, String)} to store the cast java * source string in to the current {@link OgnlContext}. This will either add to the existing * string present if it already exists or create a new instance and store it using the static key * of {@link #PRE_CAST}. * * @param context The current execution context. * @param cast The java source string to store in to the context. */ public static > void addCastString(C context, String cast) { String value = (String) context.get(PRE_CAST); if (value != null) value = cast + value; else value = cast; context.put(PRE_CAST, value); } /** * Returns the appropriate casting expression (minus parens) for the specified class type. * *

* For instance, if given an {@link Integer} object the string "java.lang.Integer" * would be returned. For an array of primitive ints "int[]" and so on.. *

* * @param type The class to cast a string expression for. * @return The converted raw string version of the class name. */ public static String getCastString(Class type) { if (type == null) return null; return type.isArray() ? type.getComponentType().getName() + "[]" : type.getName(); } /** * Convenience method called by many different property/method resolving AST types to get a root expression * resolving string for the given node. The callers are mostly ignorant and rely on this method to properly * determine if the expression should be cast at all and take the appropriate actions if it should. * * @param expression The node to check and generate a root expression to if necessary. * @param root The root object for this execution. * @param context The current execution context. * @return Either an empty string or a root path java source string compatible with javassist compilations * from the root object up to the specified {@link Node}. */ public static > String getRootExpression(Node expression, Object root, C context) { String rootExpr = ""; if (!shouldCast(expression)) return rootExpr; if ((!(expression instanceof ASTList) && !(expression instanceof ASTVarRef) && !(expression instanceof ASTStaticMethod) && !(expression instanceof ASTStaticField) && !(expression instanceof ASTConst) && !(expression instanceof ExpressionNode) && !(expression instanceof ASTCtor) && root != null) || (root != null && expression instanceof ASTRootVarRef)) { Class castClass = OgnlRuntime.getCompiler().getRootExpressionClass(expression, context); if (castClass.isArray() || expression instanceof ASTRootVarRef) { rootExpr = "((" + getCastString(castClass) + ")$2)"; if (expression instanceof ASTProperty && !((ASTProperty) expression).isIndexedAccess()) { rootExpr += "."; } } else if ((expression instanceof ASTProperty && ((ASTProperty) expression).isIndexedAccess()) || expression instanceof ASTChain) { rootExpr = "((" + getCastString(castClass) + ")$2)"; } else { rootExpr = "((" + getCastString(castClass) + ")$2)."; } } return rootExpr; } /** * Used by {@link #getRootExpression(Node, Object, OgnlContext)} to determine if the expression * needs to be cast at all. * * @param expression The node to check against. * @return Yes if the node type should be cast - false otherwise. */ public static > boolean shouldCast(Node expression) { if (expression instanceof ASTChain) { Node child = expression.jjtGetChild(0); if (child instanceof ASTConst || child instanceof ASTStaticMethod || child instanceof ASTStaticField || (child instanceof ASTVarRef && !(child instanceof ASTRootVarRef))) return false; } return !(expression instanceof ASTConst); } public String castExpression(C context, Node expression, String body) { // ok - so this looks really f-ed up ...and it is ..eh if you can do it better I'm all for it :) if (context.getCurrentAccessor() == null || context.getPreviousType() == null || context.getCurrentAccessor().isAssignableFrom(context.getPreviousType()) || (context.getCurrentType() != null && context.getCurrentObject() != null && context.getCurrentType().isAssignableFrom(context.getCurrentObject().getClass()) && context.getCurrentAccessor().isAssignableFrom(context.getPreviousType())) || body == null || body.trim().isEmpty() || (context.getCurrentType() != null && context.getCurrentType().isArray() && (context.getPreviousType() == null || context.getPreviousType() != Object.class)) || expression instanceof ASTOr || expression instanceof ASTAnd || expression instanceof ASTRootVarRef || context.getCurrentAccessor() == Class.class || (context.get(ExpressionCompiler.PRE_CAST) != null && ((String) context.get(ExpressionCompiler.PRE_CAST)).startsWith("new")) || expression instanceof ASTStaticField || expression instanceof ASTStaticMethod || (expression instanceof OrderedReturn && ((OrderedReturn) expression).getLastExpression() != null)) return body; ExpressionCompiler.addCastString(context, "((" + ExpressionCompiler.getCastString(context.getCurrentAccessor()) + ")"); return ")" + body; } public String getClassName(Class clazz) { if (clazz.getName().equals("java.util.AbstractList$Itr")) return Iterator.class.getName(); if (Modifier.isPublic(clazz.getModifiers()) && clazz.isInterface()) return clazz.getName(); return getClassName(clazz, clazz.getInterfaces()); } private String getClassName(Class clazz, Class[] interfaces) { for (Class anInterface : interfaces) { if (anInterface.getName().indexOf("util.List") > 0) { return anInterface.getName(); } else if (anInterface.getName().indexOf("Iterator") > 0) { return anInterface.getName(); } } final Class superClazz = clazz.getSuperclass(); if (superClazz != null) { final Class[] superClazzInterfaces = superClazz.getInterfaces(); if (superClazzInterfaces.length > 0) return getClassName(superClazz, superClazzInterfaces); } return clazz.getName(); } public Class getSuperOrInterfaceClass(Method method, Class clazz) { Class[] interfaces = clazz.getInterfaces(); if (interfaces.length > 0) { Class intClass; for (Class anInterface : interfaces) { intClass = getSuperOrInterfaceClass(method, anInterface); if (intClass != null) { return intClass; } if (Modifier.isPublic(anInterface.getModifiers()) && containsMethod(method, anInterface)) { return anInterface; } } } if (clazz.getSuperclass() != null) { Class superClass = getSuperOrInterfaceClass(method, clazz.getSuperclass()); if (superClass != null) return superClass; } if (Modifier.isPublic(clazz.getModifiers()) && containsMethod(method, clazz)) return clazz; return null; } /** * Helper utility method used by compiler to help resolve class->method mappings * during method calls to {@link OgnlExpressionCompiler#getSuperOrInterfaceClass(java.lang.reflect.Method, Class)}. * * @param method The method to check for existance of. * @param clazz The class to check for the existance of a matching method definition to the method passed in. * @return True if the class contains the specified method, false otherwise. */ public boolean containsMethod(Method method, Class clazz) { Method[] methods = clazz.getMethods(); for (Method value : methods) { if (value.getName().equals(method.getName()) && value.getReturnType() == method.getReturnType()) { Class[] parms = method.getParameterTypes(); Class[] methodParams = value.getParameterTypes(); if (methodParams.length != parms.length) continue; boolean parmsMatch = true; for (int p = 0; p < parms.length; p++) { if (parms[p] != methodParams[p]) { parmsMatch = false; break; } } if (!parmsMatch) continue; Class[] exceptions = method.getExceptionTypes(); Class[] methodExceptions = value.getExceptionTypes(); if (methodExceptions.length != exceptions.length) { continue; } boolean exceptionsMatch = true; for (int e = 0; e < exceptions.length; e++) { if (exceptions[e] != methodExceptions[e]) { exceptionsMatch = false; break; } } if (!exceptionsMatch) continue; return true; } } return false; } public Class getInterfaceClass(Class clazz) { if (clazz.getName().equals("java.util.AbstractList$Itr")) return Iterator.class; if (Modifier.isPublic(clazz.getModifiers()) && clazz.isInterface() || clazz.isPrimitive()) { return clazz; } return getInterfaceClass(clazz, clazz.getInterfaces()); } private Class getInterfaceClass(Class clazz, Class[] interfaces) { for (Class anInterface : interfaces) { if (List.class.isAssignableFrom(anInterface)) return List.class; else if (Iterator.class.isAssignableFrom(anInterface)) return Iterator.class; else if (Map.class.isAssignableFrom(anInterface)) return Map.class; else if (Set.class.isAssignableFrom(anInterface)) return Set.class; else if (Collection.class.isAssignableFrom(anInterface)) return Collection.class; } final Class superClazz = clazz.getSuperclass(); if (superClazz != null) { final Class[] superClazzInterfaces = superClazz.getInterfaces(); if (superClazzInterfaces.length > 0) return getInterfaceClass(superClazz, superClazzInterfaces); } return clazz; } public Class getRootExpressionClass(Node rootNode, C context) { if (context.getRoot() == null) { return null; } Class ret = context.getRoot().getClass(); if (context.getFirstAccessor() != null && context.getFirstAccessor().isInstance(context.getRoot())) { ret = context.getFirstAccessor(); } return ret; } public void compileExpression(C context, Node expression, Object root) throws Exception { if (expression.getAccessor() != null) { return; } String getBody, setBody; EnhancedClassLoader loader = getClassLoader(context); ClassPool pool = getClassPool(context, loader); CtClass newClass = pool.makeClass(expression.getClass().getName() + expression.hashCode() + classCounter++ + "Accessor"); newClass.addInterface(getCtClass(ExpressionAccessor.class)); CtClass ognlClass = getCtClass(OgnlContext.class); CtClass objClass = getCtClass(Object.class); CtMethod valueGetter = new CtMethod(objClass, "get", new CtClass[]{ognlClass, objClass}, newClass); CtMethod valueSetter = new CtMethod(CtClass.voidType, "set", new CtClass[]{ognlClass, objClass, objClass}, newClass); CtField nodeMember = null; // will only be set if uncompilable exception is thrown CtClass nodeClass = getCtClass(Node.class); CtMethod setExpression = null; try { getBody = generateGetter(context, newClass, pool, valueGetter, expression, root); } catch (UnsupportedCompilationException uc) { nodeMember = new CtField(nodeClass, "_node", newClass); newClass.addField(nodeMember); getBody = generateOgnlGetter(newClass, valueGetter, nodeMember); setExpression = CtNewMethod.setter("setExpression", nodeMember); newClass.addMethod(setExpression); } try { setBody = generateSetter(context, newClass, pool, valueSetter, expression, root); } catch (UnsupportedCompilationException uc) { if (nodeMember == null) { nodeMember = new CtField(nodeClass, "_node", newClass); newClass.addField(nodeMember); } setBody = generateOgnlSetter(newClass, valueSetter, nodeMember); if (setExpression == null) { setExpression = CtNewMethod.setter("setExpression", nodeMember); newClass.addMethod(setExpression); } } try { newClass.addConstructor(CtNewConstructor.defaultConstructor(newClass)); Class clazz = instantiateClass(pool, newClass); newClass.detach(); expression.setAccessor((ExpressionAccessor) clazz.getDeclaredConstructor().newInstance()); // need to set expression on node if the field was just defined. if (nodeMember != null) { expression.getAccessor().setExpression(expression); } } catch (Throwable t) { throw new RuntimeException("Error compiling expression on object " + root + " with expression node " + expression + " getter body: " + getBody + " setter body: " + setBody, t); } } /** * Called when newClass has been fully populated and is ready to be instantiated. * * @param pool the javassist ClassPool context * @param newClass the definition of the new class * @return The compiled class * @throws CannotCompileException if thrown by javassist */ protected Class instantiateClass(final ClassPool pool, final CtClass newClass) throws CannotCompileException { return pool.toClass(newClass); } protected String generateGetter(C context, CtClass newClass, ClassPool pool, CtMethod valueGetter, Node expression, Object root) throws Exception { String pre = ""; String post = ""; String body; context.setRoot(root); // the ExpressionAccessor API has to reference the generic Object class for get/set operations, so this sets up that known // type beforehand context.remove(PRE_CAST); // Recursively generate the java source code representation of the top level expression String getterCode = expression.toGetSourceString(context, root); if (getterCode == null || getterCode.trim().length() <= 0 && !ASTVarRef.class.isAssignableFrom(expression.getClass())) getterCode = "null"; String castExpression = (String) context.get(PRE_CAST); if (context.getCurrentType() == null || context.getCurrentType().isPrimitive() || Character.class.isAssignableFrom(context.getCurrentType()) || Object.class == context.getCurrentType()) { pre = pre + " ($w) ("; post = post + ")"; } String rootExpr = !getterCode.equals("null") ? getRootExpression(expression, root, context) : ""; String noRoot = (String) context.remove("_noRoot"); if (noRoot != null) { rootExpr = ""; } createLocalReferences(context, pool, newClass, valueGetter.getParameterTypes()); if (expression instanceof OrderedReturn && ((OrderedReturn) expression).getLastExpression() != null) { body = "{ " + (expression instanceof ASTMethod || expression instanceof ASTChain ? rootExpr : "") + (castExpression != null ? castExpression : "") + ((OrderedReturn) expression).getCoreExpression() + " return " + pre + ((OrderedReturn) expression).getLastExpression() + post + ";}"; } else { body = "{ return " + pre + (castExpression != null ? castExpression : "") + rootExpr + getterCode + post + ";}"; } if (body.contains("..")) { body = body.replaceAll("\\.\\.", "."); } valueGetter.setBody(body); newClass.addMethod(valueGetter); return body; } public String createLocalReference(C context, String expression, Class type) { String referenceName = "ref" + context.incrementLocalReferenceCounter(); context.addLocalReference(referenceName, new OgnlLocalReference(referenceName, expression, type)); String castString = ""; if (!type.isPrimitive()) castString = "(" + ExpressionCompiler.getCastString(type) + ") "; return castString + referenceName + "($$)"; } private void createLocalReferences(C context, ClassPool pool, CtClass clazz, CtClass[] params) throws CannotCompileException, NotFoundException { Map referenceMap = context.getLocalReferences(); if (referenceMap == null || referenceMap.isEmpty()) { return; } Iterator it = referenceMap.values().iterator(); while (it.hasNext()) { LocalReference ref = it.next(); String widener = ref.getType().isPrimitive() ? " " : " ($w) "; String body = "{"; body += " return " + widener + ref.getExpression() + ";"; body += "}"; if (body.contains("..")) { body = body.replaceAll("\\.\\.", "."); } CtMethod method = new CtMethod(pool.get(getCastString(ref.getType())), ref.getName(), params, clazz); method.setBody(body); clazz.addMethod(method); it.remove(); } } protected String generateSetter(C context, CtClass newClass, ClassPool pool, CtMethod valueSetter, Node expression, Object root) throws Exception { if (expression instanceof ExpressionNode || expression instanceof ASTConst) { throw new UnsupportedCompilationException("Can't compile expression/constant setters."); } context.setRoot(root); context.remove(PRE_CAST); String body; String setterCode = expression.toSetSourceString(context, root); String castExpression = (String) context.get(PRE_CAST); if (setterCode == null || setterCode.trim().isEmpty()) throw new UnsupportedCompilationException("Can't compile null setter body."); if (root == null) throw new UnsupportedCompilationException("Can't compile setters with a null root object."); String pre = getRootExpression(expression, root, context); String noRoot = (String) context.remove("_noRoot"); if (noRoot != null) pre = ""; createLocalReferences(context, pool, newClass, valueSetter.getParameterTypes()); body = "{" + (castExpression != null ? castExpression : "") + pre + setterCode + ";}"; if (body.contains("..")) { body = body.replaceAll("\\.\\.", "."); } valueSetter.setBody(body); newClass.addMethod(valueSetter); return body; } /** * Fail safe getter creation when normal compilation fails. * * @param clazz The javassist class the new method should be attached to. * @param valueGetter The method definition the generated code will be contained within. * @param node The root expression node. * @return The generated source string for this method, the method will still be * added via the javassist API either way so this is really a convenience * for exception reporting / debugging. * @throws Exception If a javassist error occurs. */ protected String generateOgnlGetter(CtClass clazz, CtMethod valueGetter, CtField node) throws Exception { String body = "return " + node.getName() + ".getValue($1, $2);"; valueGetter.setBody(body); clazz.addMethod(valueGetter); return body; } /** * Fail safe setter creation when normal compilation fails. * * @param clazz The javassist class the new method should be attached to. * @param valueSetter The method definition the generated code will be contained within. * @param node The root expression node. * @return The generated source string for this method, the method will still be * added via the javassist API either way so this is really a convenience * for exception reporting / debugging. * @throws Exception If a javassist error occurs. */ protected String generateOgnlSetter(CtClass clazz, CtMethod valueSetter, CtField node) throws Exception { String body = node.getName() + ".setValue($1, $2, $3);"; valueSetter.setBody(body); clazz.addMethod(valueSetter); return body; } /** * Creates a {@link ClassLoader} instance compatible with the javassist classloader and normal * OGNL class resolving semantics. * * @param context The current execution context. * @return The created {@link ClassLoader} instance. */ protected EnhancedClassLoader getClassLoader(C context) { EnhancedClassLoader ret = loaders.get(context.getClassResolver()); if (ret != null) { return ret; } ClassLoader classLoader = new ContextClassLoader<>(OgnlContext.class.getClassLoader(), context); ret = new EnhancedClassLoader(classLoader); loaders.put(context.getClassResolver(), ret); return ret; } /** * Loads a new class definition via javassist for the specified class. * * @param searchClass The class to load. * @return The javassist class equivalent. * @throws NotFoundException When the class definition can't be found. */ protected CtClass getCtClass(Class searchClass) throws NotFoundException { return classPool.get(searchClass.getName()); } /** * Gets either a new or existing {@link ClassPool} for use in compiling javassist * classes. A new class path object is inserted in to the returned {@link ClassPool} using * the passed in loader instance if a new pool needs to be created. * * @param context The current execution context. * @param loader The {@link ClassLoader} instance to use - as returned by {@link #getClassLoader(OgnlContext)}. * @return The existing or new {@link ClassPool} instance. */ protected ClassPool getClassPool(C context, EnhancedClassLoader loader) { if (classPool != null) { return classPool; } classPool = ClassPool.getDefault(); classPool.insertClassPath(new LoaderClassPath(loader.getParent())); return classPool; } } ================================================ FILE: ognl/src/main/java/ognl/enhance/LocalReference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; /** * Container class for {@link OgnlExpressionCompiler} generated local method * block references. */ public interface LocalReference { /** * The name of the assigned variable reference. * * @return The name of the reference as it will be when compiled. */ String getName(); /** * The expression that sets the value, ie the part after <class type> refName = <expression>. * * @return The setting expression. */ String getExpression(); /** * The type of reference. * * @return The type. */ Class getType(); } ================================================ FILE: ognl/src/main/java/ognl/enhance/OgnlExpressionCompiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; import ognl.ASTChain; import ognl.Node; import ognl.OgnlContext; import java.lang.reflect.Method; /** * Core interface implemented by expression compiler instances. */ public interface OgnlExpressionCompiler> { /** * Static constant used in conjunction with {@link OgnlContext} to store temporary references. */ String ROOT_TYPE = "-ognl-root-type"; /** * The core method executed to compile a specific expression. It is expected that this expression * always return a {@link Node} with a non null {@link Node#getAccessor()} instance - unless an exception * is thrown by the method or the statement wasn't compilable in this instance because of missing/null objects * in the expression. These instances may in some cases continue to call this compilation method until the expression * is resolvable. * * @param context The context of execution. * @param expression The pre-parsed root expression node to compile. * @param root The root object for the expression - may be null in many instances so some implementations * may exit * @throws Exception If an error occurs compiling the expression and no strategy has been implemented to handle incremental * expression compilation for incomplete expression members. */ void compileExpression(C context, Node expression, Object root) throws Exception; /** * Gets a javassist safe class string for the given class instance. This is especially * useful for handling array vs. normal class casting strings. * * @param clazz The class to get a string equivalent javassist compatible string reference for. * @return The string equivalent of the class. */ String getClassName(Class clazz); /** * Used in places where the preferred {@link #getSuperOrInterfaceClass(java.lang.reflect.Method, Class)} isn't possible * because the method isn't known for a class. Attempts to upcast the given class to the next available non-private accessible * class so that compiled expressions can reference the interface class of an instance so as not to be compiled in to overly * specific statements. * * @param clazz The class to attempt to find a compatible interface for. * @return The same class if no higher level interface could be matched against or the interface equivalent class. */ Class getInterfaceClass(Class clazz); /** * For the given {@link Method} and class finds the highest level interface class this combination can be cast to. * * @param method The method the class must implement. * @param clazz The current class being worked with. * @return The highest level interface / class that the referenced {@link Method} is declared in. */ Class getSuperOrInterfaceClass(Method method, Class clazz); /** * For a given root object type returns the base class type to be used in root referenced expressions. This * helps in some instances where the root objects themselves are compiled javassist instances that need more generic * class equivalents to cast to. * * @param rootNode The root expression node. * @param context The current execution context. * @return The root expression class type to cast to for this node. */ Class getRootExpressionClass(Node rootNode, C context); /** * Used primarily by AST types like {@link ASTChain} where foo.bar.id type references * may need to be cast multiple times in order to properly resolve the members in a compiled statement. * *

* This method should be using the various {@link OgnlContext#getCurrentType()} / {@link OgnlContext#getCurrentAccessor()} methods * to inspect the type stack and properly cast to the right classes - but only when necessary. *

* * @param context The current execution context. * @param expression The node being checked for casting. * @param body The java source string generated by the given node. * @return The body string parameter plus any additional casting syntax needed to make the expression * resolvable. */ String castExpression(C context, Node expression, String body); /** * Method is used for expressions where multiple inner parameter method calls in generated java source strings * cause javassit failures. It is hacky and cumbersome to have to generate expressions this way but it's the only * current known way to make javassist happy. * *

* Takes an expression block generated by a node and creates a new method on the base object being compiled so that * sufficiently complicated sub expression blocks can be broken out in to distinct methods to be referenced by the core * accessor / setter methods in the base compiled root object. *

* * @param context The current execution context. * @param expression The java source expression to dump in to a seperate method reference. * @param type The return type that should be specified for the new method. * @return The method name that will be used to reference the sub expression in place of the actual sub expression * itself. */ String createLocalReference(C context, String expression, Class type); } ================================================ FILE: ognl/src/main/java/ognl/enhance/OgnlLocalReference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; import java.util.Objects; /** * Implementation of {@link LocalReference}. */ public class OgnlLocalReference implements LocalReference { private final String name; private final Class clazzType; private final String expression; public OgnlLocalReference(String name, String expression, Class clazzType) { this.name = name; this.clazzType = clazzType; this.expression = expression; } public String getName() { return name; } public String getExpression() { return expression; } public Class getType() { return clazzType; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OgnlLocalReference that = (OgnlLocalReference) o; if (!Objects.equals(expression, that.expression)) { return false; } if (!Objects.equals(name, that.name)) { return false; } return Objects.equals(clazzType, that.clazzType); } public int hashCode() { int result; result = (name != null ? name.hashCode() : 0); result = 31 * result + (clazzType != null ? clazzType.hashCode() : 0); result = 31 * result + (expression != null ? expression.hashCode() : 0); return result; } public String toString() { return "LocalReferenceImpl[" + "_name='" + name + '\'' + '\n' + ", _type=" + clazzType + '\n' + ", _expression='" + expression + '\'' + '\n' + ']'; } } ================================================ FILE: ognl/src/main/java/ognl/enhance/OrderedReturn.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; import ognl.Node; /** * Marks an ognl expression {@link Node} as needing to have the return portion of a * getter method happen in a specific part of the generated expression vs just having * the whole expression returned in one chunk. */ public interface OrderedReturn { /** * Get the core expression to execute first before any return foo logic is started. * * @return The core standalone expression that shouldn't be pre-pended with a return keyword. */ String getCoreExpression(); /** * Gets the last expression to be pre-pended with a return <expression> block. * * @return The expression representing the return portion of a statement; */ String getLastExpression(); } ================================================ FILE: ognl/src/main/java/ognl/enhance/UnsupportedCompilationException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * and/or LICENSE file distributed with this work for additional * information regarding copyright ownership. The ASF licenses * this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.enhance; /** * Thrown during bytecode enhancement conversions of ognl expressions to indicate * that a certain expression isn't currently supported as a pure java bytecode enhanced * version. * *

* If this exception is thrown it is expected that ognl will fall back to default ognl * evaluation of the expression. *

*/ public class UnsupportedCompilationException extends RuntimeException { private static final long serialVersionUID = 37018630558258414L; public UnsupportedCompilationException(String message) { super(message); } public UnsupportedCompilationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: ognl/src/main/java/ognl/internal/Cache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal; public interface Cache { void clear(); int getSize(); V get(K key) throws CacheException; V put(K key, V value); } ================================================ FILE: ognl/src/main/java/ognl/internal/CacheException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal; public class CacheException extends RuntimeException { private static final long serialVersionUID = 3909399641531596633L; public CacheException(Throwable e) { super(e.getMessage(), e); } } ================================================ FILE: ognl/src/main/java/ognl/internal/CacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal; import ognl.internal.entry.CacheEntryFactory; import ognl.internal.entry.ClassCacheEntryFactory; public interface CacheFactory { Cache createCache(CacheEntryFactory entryFactory); ClassCache createClassCache(); ClassCache createClassCache(ClassCacheEntryFactory entryFactory); } ================================================ FILE: ognl/src/main/java/ognl/internal/ClassCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal; import ognl.ClassCacheInspector; /** * This is a highly specialized map for storing values keyed by Class objects. */ public interface ClassCache extends Cache, V> { void setClassInspector(ClassCacheInspector inspector); } ================================================ FILE: ognl/src/main/java/ognl/internal/ClassCacheHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal; public class ClassCacheHandler { private ClassCacheHandler() { } public static T getHandler(Class forClass, ClassCache handlers) throws CacheException { T answer = handlers.get(forClass); if (answer != null) { return answer; } synchronized (handlers) { answer = handlers.get(forClass); if (answer == null) { Class keyFound; if (forClass.isArray()) { answer = handlers.get(Object[].class); keyFound = null; } else { keyFound = forClass; outer: for (Class clazz = forClass; clazz != null; clazz = clazz.getSuperclass()) { answer = handlers.get(clazz); if (answer != null) { keyFound = clazz; break; } Class[] interfaces = clazz.getInterfaces(); for (Class iface : interfaces) { answer = handlers.get(iface); if (answer == null) { /* Try super-interfaces */ answer = getHandler(iface, handlers); } if (answer != null) { keyFound = iface; break outer; } } } } if (answer != null && keyFound != forClass) { handlers.put(forClass, answer); } } } return answer; } } ================================================ FILE: ognl/src/main/java/ognl/internal/HashMapCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal; import ognl.internal.entry.CacheEntryFactory; import java.util.HashMap; import java.util.Map; public class HashMapCache implements Cache { private final Map cache = new HashMap<>(512); private final CacheEntryFactory cacheEntryFactory; public HashMapCache(CacheEntryFactory cacheEntryFactory) { this.cacheEntryFactory = cacheEntryFactory; } public void clear() { synchronized (cache) { cache.clear(); } } public int getSize() { synchronized (cache) { return cache.size(); } } public V get(K key) throws CacheException { V v = cache.get(key); if (shouldCreate(cacheEntryFactory, v)) { synchronized (cache) { v = cache.get(key); if (v != null) { return v; } return put(key, cacheEntryFactory.create(key)); } } return v; } protected boolean shouldCreate(CacheEntryFactory cacheEntryFactory, V v) throws CacheException { return cacheEntryFactory != null && v == null; } public V put(K key, V value) { synchronized (cache) { cache.put(key, value); return value; } } public boolean contains(K key) { return this.cache.containsKey(key); } } ================================================ FILE: ognl/src/main/java/ognl/internal/HashMapCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal; import ognl.internal.entry.CacheEntryFactory; import ognl.internal.entry.ClassCacheEntryFactory; public class HashMapCacheFactory implements CacheFactory { public Cache createCache(CacheEntryFactory entryFactory) { return new HashMapCache<>(entryFactory); } public ClassCache createClassCache() { return createClassCache(null); } public ClassCache createClassCache(ClassCacheEntryFactory entryFactory) { return new HashMapClassCache<>(entryFactory); } } ================================================ FILE: ognl/src/main/java/ognl/internal/HashMapClassCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal; import ognl.ClassCacheInspector; import ognl.internal.entry.CacheEntryFactory; public class HashMapClassCache extends HashMapCache, T> implements ClassCache { private ClassCacheInspector inspector; public HashMapClassCache(CacheEntryFactory, T> entryFactory) { super(entryFactory); } public void setClassInspector(ClassCacheInspector inspector) { this.inspector = inspector; } public T put(Class key, T value) { if (inspector != null && !inspector.shouldCache(key)) { return value; } return super.put(key, value); } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/CacheEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; public interface CacheEntry { } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/CacheEntryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; import ognl.internal.CacheException; public interface CacheEntryFactory { V create(K key) throws CacheException; } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/ClassCacheEntryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; public interface ClassCacheEntryFactory extends CacheEntryFactory, T> { } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/DeclaredMethodCacheEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; public class DeclaredMethodCacheEntry extends MethodCacheEntry { MethodType type; public enum MethodType { STATIC, NON_STATIC } public DeclaredMethodCacheEntry(Class targetClass) { super(targetClass); } public DeclaredMethodCacheEntry(Class targetClass, MethodType type) { super(targetClass); this.type = type; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof DeclaredMethodCacheEntry)) { return false; } if (!super.equals(o)) { return false; } DeclaredMethodCacheEntry that = (DeclaredMethodCacheEntry) o; return type == that.type; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (type != null ? type.hashCode() : 0); return result; } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/DeclaredMethodCacheEntryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class DeclaredMethodCacheEntryFactory extends MethodCacheEntryFactory { @Override protected boolean shouldCache(DeclaredMethodCacheEntry key, Method method) { if (key.type == null) { return true; } boolean isStatic = Modifier.isStatic(method.getModifiers()); if (key.type == DeclaredMethodCacheEntry.MethodType.STATIC) { return isStatic; } return !isStatic; } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/FieldCacheEntryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; import ognl.internal.CacheException; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class FieldCacheEntryFactory implements ClassCacheEntryFactory> { public Map create(Class key) throws CacheException { Field[] declaredFields = key.getDeclaredFields(); Map result = new HashMap<>(declaredFields.length); for (Field field : declaredFields) { result.put(field.getName(), field); } return result; } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/GenericMethodParameterTypeCacheEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; import java.lang.reflect.Method; public class GenericMethodParameterTypeCacheEntry implements CacheEntry { final Method method; final Class type; public GenericMethodParameterTypeCacheEntry(Method method, Class type) { this.method = method; this.type = type; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GenericMethodParameterTypeCacheEntry)) { return false; } GenericMethodParameterTypeCacheEntry that = (GenericMethodParameterTypeCacheEntry) o; return method.equals(that.method) && type.equals(that.type); } @Override public int hashCode() { int result = method.hashCode(); result = 31 * result + type.hashCode(); return result; } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/GenericMethodParameterTypeFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; import ognl.internal.CacheException; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; public class GenericMethodParameterTypeFactory implements CacheEntryFactory[]> { public Class[] create(GenericMethodParameterTypeCacheEntry entry) throws CacheException { Class[] types; ParameterizedType param = (ParameterizedType) entry.type.getGenericSuperclass(); Type[] genTypes = entry.method.getGenericParameterTypes(); TypeVariable[] declaredTypes = entry.method.getDeclaringClass().getTypeParameters(); types = new Class[genTypes.length]; for (int i = 0; i < genTypes.length; i++) { TypeVariable paramType = null; if (genTypes[i] instanceof TypeVariable) { paramType = (TypeVariable) genTypes[i]; } else if (genTypes[i] instanceof GenericArrayType) { paramType = (TypeVariable) ((GenericArrayType) genTypes[i]).getGenericComponentType(); } else if (genTypes[i] instanceof ParameterizedType) { types[i] = (Class) ((ParameterizedType) genTypes[i]).getRawType(); continue; } else if (genTypes[i] instanceof Class) { types[i] = (Class) genTypes[i]; continue; } Class resolved = resolveType(param, paramType, declaredTypes); if (resolved != null) { if (genTypes[i] instanceof GenericArrayType) { resolved = Array.newInstance(resolved, 0).getClass(); } types[i] = resolved; continue; } types[i] = entry.method.getParameterTypes()[i]; } return types; } private Class resolveType(ParameterizedType param, TypeVariable var, TypeVariable[] declaredTypes) { if (param.getActualTypeArguments().length < 1) { return null; } for (int i = 0; i < declaredTypes.length; i++) { if (!(param.getActualTypeArguments()[i] instanceof TypeVariable) && declaredTypes[i].getName().equals(var.getName())) { return (Class) param.getActualTypeArguments()[i]; } } return null; } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/MethodAccessCacheEntryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; import ognl.internal.CacheException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class MethodAccessCacheEntryFactory implements CacheEntryFactory { public static final MethodAccessEntryValue INACCESSIBLE_NON_PUBLIC_METHOD = new MethodAccessEntryValue(false, true); public static final MethodAccessEntryValue ACCESSIBLE_NON_PUBLIC_METHOD = new MethodAccessEntryValue(true, true); public static final MethodAccessEntryValue PUBLIC_METHOD = new MethodAccessEntryValue(true); public MethodAccessEntryValue create(Method method) throws CacheException { final boolean notPublic = !Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()); if (!notPublic) { return PUBLIC_METHOD; } if (!method.isAccessible()) { return INACCESSIBLE_NON_PUBLIC_METHOD; } return ACCESSIBLE_NON_PUBLIC_METHOD; } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/MethodAccessEntryValue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; public class MethodAccessEntryValue { private final boolean isAccessible; private boolean notPublic; public MethodAccessEntryValue(boolean accessible) { this.isAccessible = accessible; } public MethodAccessEntryValue(boolean accessible, boolean notPublic) { isAccessible = accessible; this.notPublic = notPublic; } public boolean isAccessible() { return isAccessible; } public boolean isNotPublic() { return notPublic; } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/MethodCacheEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; public class MethodCacheEntry implements CacheEntry { public final Class targetClass; public MethodCacheEntry(Class targetClass) { this.targetClass = targetClass; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof MethodCacheEntry)) { return false; } MethodCacheEntry that = (MethodCacheEntry) o; return targetClass.equals(that.targetClass); } @Override public int hashCode() { return targetClass.hashCode(); } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/MethodCacheEntryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; import ognl.OgnlRuntime; import ognl.internal.CacheException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public abstract class MethodCacheEntryFactory implements CacheEntryFactory>> { public Map> create(T key) throws CacheException { Map> result = new HashMap<>(23); collectMethods(key, key.targetClass, result); return result; } protected abstract boolean shouldCache(T key, Method method); private void collectMethods(T key, Class c, Map> result) { Method[] ma; try { ma = c.getDeclaredMethods(); } catch (SecurityException ignored) { ma = c.getMethods(); } for (Method method : ma) { if (!OgnlRuntime.isMethodCallable(method)) continue; if (shouldCache(key, method)) { List ml = result.computeIfAbsent(method.getName(), k -> new ArrayList<>()); ml.add(method); } } final Class superclass = c.getSuperclass(); if (superclass != null) { collectMethods(key, superclass, result); } for (final Class iface : c.getInterfaces()) { collectMethods(key, iface, result); } } } ================================================ FILE: ognl/src/main/java/ognl/internal/entry/PropertyDescriptorCacheEntryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.internal.entry; import ognl.ObjectIndexedPropertyDescriptor; import ognl.OgnlException; import ognl.OgnlRuntime; import ognl.internal.CacheException; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class PropertyDescriptorCacheEntryFactory implements ClassCacheEntryFactory> { public Map create(Class targetClass) throws CacheException { Map result = new HashMap<>(101); PropertyDescriptor[] pda; try { pda = Introspector.getBeanInfo(targetClass).getPropertyDescriptors(); for (PropertyDescriptor aPda : pda) { // workaround for Introspector bug 6528714 (bugs.sun.com) if (aPda.getReadMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getReadMethod())) { aPda.setReadMethod( findClosestMatchingMethod(targetClass, aPda.getReadMethod(), aPda.getName(), aPda.getPropertyType(), true)); } if (aPda.getWriteMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getWriteMethod())) { aPda.setWriteMethod( findClosestMatchingMethod(targetClass, aPda.getWriteMethod(), aPda.getName(), aPda.getPropertyType(), false)); } result.put(aPda.getName(), aPda); } findObjectIndexedPropertyDescriptors(targetClass, result); } catch (IntrospectionException | OgnlException e) { throw new CacheException(e); } return result; } static Method findClosestMatchingMethod( Class targetClass, Method method, String propertyName, Class propertyType, boolean isReadMethod ) throws OgnlException { List methods = OgnlRuntime.getDeclaredMethods(targetClass, propertyName, !isReadMethod); for (Method closestMethod : methods) { if (closestMethod.getName().equals(method.getName()) && method.getReturnType().isAssignableFrom(method.getReturnType()) && closestMethod.getReturnType() == propertyType && closestMethod.getParameterTypes().length == method.getParameterTypes().length) { return closestMethod; } } return method; } private static void findObjectIndexedPropertyDescriptors( Class targetClass, Map intoMap ) throws OgnlException { Map> allMethods = OgnlRuntime.getMethods(targetClass, false); Map> pairs = new HashMap<>(101); for (Map.Entry> entry : allMethods.entrySet()) { String methodName = entry.getKey(); List methods = entry.getValue(); /* * Only process set/get where there is exactly one implementation of the method per class and those * implementations are all the same */ if (indexMethodCheck(methods)) { boolean isGet = false, isSet; Method method = methods.get(0); if (((isSet = methodName.startsWith(OgnlRuntime.SET_PREFIX)) || (isGet = methodName.startsWith(OgnlRuntime.GET_PREFIX))) && (methodName.length() > 3)) { String propertyName = Introspector.decapitalize(methodName.substring(3)); Class[] parameterTypes = OgnlRuntime.getParameterTypes(method); int parameterCount = parameterTypes.length; if (isGet && (parameterCount == 1) && (method.getReturnType() != Void.TYPE)) { List pair = pairs.computeIfAbsent(propertyName, k -> new ArrayList<>()); pair.add(method); } if (isSet && (parameterCount == 2) && (method.getReturnType() == Void.TYPE)) { List pair = pairs.computeIfAbsent(propertyName, k -> new ArrayList<>()); pair.add(method); } } } } for (Map.Entry> entry : pairs.entrySet()) { String propertyName = entry.getKey(); List methods = entry.getValue(); if (methods.size() == 2) { Method method1 = methods.get(0), method2 = methods.get(1), setMethod = (method1.getParameterTypes().length == 2) ? method1 : method2, getMethod = (setMethod == method1) ? method2 : method1; Class keyType = getMethod.getParameterTypes()[0], propertyType = getMethod.getReturnType(); if (keyType == setMethod.getParameterTypes()[0] && propertyType == setMethod.getParameterTypes()[1]) { ObjectIndexedPropertyDescriptor propertyDescriptor; try { propertyDescriptor = new ObjectIndexedPropertyDescriptor(propertyName, propertyType, getMethod, setMethod); } catch (Exception ex) { throw new OgnlException( "creating object indexed property descriptor for '" + propertyName + "' in " + targetClass, ex); } intoMap.put(propertyName, propertyDescriptor); } } } } private static boolean indexMethodCheck(List methods) { boolean result = false; if (!methods.isEmpty()) { Method method = methods.get(0); Class[] parameterTypes = OgnlRuntime.getParameterTypes(method); int numParameterTypes = parameterTypes.length; Class lastMethodClass = method.getDeclaringClass(); result = true; for (int i = 1; result && (i < methods.size()); i++) { Class clazz = methods.get(i).getDeclaringClass(); // Check to see if more than one method implemented per class if (lastMethodClass == clazz) { result = false; } else { Class[] mpt = OgnlRuntime.getParameterTypes(method); for (int j = 0; j < numParameterTypes; j++) { if (parameterTypes[j] != mpt[j]) { result = false; break; } } } lastMethodClass = clazz; } } return result; } } ================================================ FILE: ognl/src/main/java/ognl/package.html ================================================ OGNL Overview

OGNL stands for Object-Graph Navigation Language; it is an expression language for getting and setting properties of Java objects. You use the same expression for both getting and setting the value of a property.

OGNL started out as a way to set up associations between UI components and controllers using property names. As the desire for more complicated associations grew, Drew Davidson created what he called KVCL, for Key-Value Coding Language, egged on by Luke Blanshard. Luke then reimplemented the language using ANTLR, came up with the new name, and, egged on by Drew, filled it out to its current state.

We pronounce OGNL as a word, like the last syllables of a drunken pronunciation of "orthogonal" or like "ogg-null".

================================================ FILE: ognl/src/main/javacc/ognl.jj ================================================ /*@bgen(jjtree) Generated By:JJTree: Do not edit this line. src/java/ognl/ognl.jj */ /*@egen*/ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT 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 file defines the syntax of OGNL, the Object-Graph Navigation Language. This * language was devised by Drew Davidson, who called it Key-Value Coding Language. Luke * Blanshard then made up the new name and reimplemented it using ANTLR, refining and * polishing the language a bit on the way. Drew maintained the system for a couple of * years; then Luke converted the ANTLR grammar to JavaCC, to eliminate the run-time * dependency on ANTLR. * * See package.html for a description of the language. */ options { // Parser options LOOKAHEAD = 1; STATIC = false; JAVA_UNICODE_ESCAPE = true; UNICODE_INPUT = true; } PARSER_BEGIN(OgnlParser) package ognl; import java.math.*; /** * OgnlParser is a JavaCC parser class; it translates OGNL expressions into abstract * syntax trees (ASTs) that can then be interpreted by the getValue and setValue methods. */ public class OgnlParser/*@bgen(jjtree)*/implements OgnlParserTreeConstants/*@egen*/ {/*@bgen(jjtree)*/ protected JJTOgnlParserState jjtree = new JJTOgnlParserState(); /*@egen*/ } PARSER_END(OgnlParser) /** * This is the top-level construct of OGNL. */ Node topLevelExpression() : {} { expression() { return jjtree.rootNode(); } } // sequence (level 14) void expression() : {} { assignmentExpression() ( ","/*@bgen(jjtree) #Sequence( 2) */ { ASTSequence jjtn001 = new ASTSequence(JJTSEQUENCE); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ assignmentExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ )* } // assignment expression (level 13) void assignmentExpression() : {} { conditionalTestExpression() [ "="/*@bgen(jjtree) #Assign( 2) */ { ASTAssign jjtn001 = new ASTAssign(JJTASSIGN); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ assignmentExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ ] } // conditional test (level 12) void conditionalTestExpression() : {} { logicalOrExpression() [ "?" conditionalTestExpression() ":"/*@bgen(jjtree) #Test( 3) */ { ASTTest jjtn001 = new ASTTest(JJTTEST); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ conditionalTestExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 3); } } /*@egen*/ ] } // logical or (||) (level 11) void logicalOrExpression() : {} { logicalAndExpression() (("||" | "or")/*@bgen(jjtree) #Or( 2) */ { ASTOr jjtn001 = new ASTOr(JJTOR); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ logicalAndExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ )* } // logical and (&&) (level 10) void logicalAndExpression() : {} { inclusiveOrExpression() (("&&" | "and")/*@bgen(jjtree) #And( 2) */ { ASTAnd jjtn001 = new ASTAnd(JJTAND); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ inclusiveOrExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ )* } // bitwise or non-short-circuiting or (|) (level 9) void inclusiveOrExpression() : {} { exclusiveOrExpression() (("|" | "bor")/*@bgen(jjtree) #BitOr( 2) */ { ASTBitOr jjtn001 = new ASTBitOr(JJTBITOR); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ exclusiveOrExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ )* } // exclusive or (^) (level 8) void exclusiveOrExpression() : {} { andExpression() (("^" | "xor")/*@bgen(jjtree) #Xor( 2) */ { ASTXor jjtn001 = new ASTXor(JJTXOR); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ andExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ )* } // bitwise or non-short-circuiting and (&) (level 7) void andExpression() : {} { equalityExpression() (("&" | "band")/*@bgen(jjtree) #BitAnd( 2) */ { ASTBitAnd jjtn001 = new ASTBitAnd(JJTBITAND); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ equalityExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ )* } // equality/inequality (==/!=) (level 6) void equalityExpression() : {} { relationalExpression() ( ("==" | "eq")/*@bgen(jjtree) #Eq( 2) */ { ASTEq jjtn001 = new ASTEq(JJTEQ); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ relationalExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ | ("!=" | "neq")/*@bgen(jjtree) #NotEq( 2) */ { ASTNotEq jjtn002 = new ASTNotEq(JJTNOTEQ); boolean jjtc002 = true; jjtree.openNodeScope(jjtn002); } try { /*@egen*/ relationalExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte002) { if (jjtc002) { jjtree.clearNodeScope(jjtn002); jjtc002 = false; } else { jjtree.popNode(); } if (jjte002 instanceof RuntimeException) { throw (RuntimeException)jjte002; } if (jjte002 instanceof ParseException) { throw (ParseException)jjte002; } throw (Error)jjte002; } finally { if (jjtc002) { jjtree.closeNodeScope(jjtn002, 2); } } /*@egen*/ )* } // boolean relational expressions (level 5) void relationalExpression() : {} { shiftExpression() ( ("<" | "lt")/*@bgen(jjtree) #Less( 2) */ { ASTLess jjtn001 = new ASTLess(JJTLESS); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ shiftExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ | (">" | "gt")/*@bgen(jjtree) #Greater( 2) */ { ASTGreater jjtn002 = new ASTGreater(JJTGREATER); boolean jjtc002 = true; jjtree.openNodeScope(jjtn002); } try { /*@egen*/ shiftExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte002) { if (jjtc002) { jjtree.clearNodeScope(jjtn002); jjtc002 = false; } else { jjtree.popNode(); } if (jjte002 instanceof RuntimeException) { throw (RuntimeException)jjte002; } if (jjte002 instanceof ParseException) { throw (ParseException)jjte002; } throw (Error)jjte002; } finally { if (jjtc002) { jjtree.closeNodeScope(jjtn002, 2); } } /*@egen*/ | ("<=" | "lte")/*@bgen(jjtree) #LessEq( 2) */ { ASTLessEq jjtn003 = new ASTLessEq(JJTLESSEQ); boolean jjtc003 = true; jjtree.openNodeScope(jjtn003); } try { /*@egen*/ shiftExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte003) { if (jjtc003) { jjtree.clearNodeScope(jjtn003); jjtc003 = false; } else { jjtree.popNode(); } if (jjte003 instanceof RuntimeException) { throw (RuntimeException)jjte003; } if (jjte003 instanceof ParseException) { throw (ParseException)jjte003; } throw (Error)jjte003; } finally { if (jjtc003) { jjtree.closeNodeScope(jjtn003, 2); } } /*@egen*/ | (">=" | "gte")/*@bgen(jjtree) #GreaterEq( 2) */ { ASTGreaterEq jjtn004 = new ASTGreaterEq(JJTGREATEREQ); boolean jjtc004 = true; jjtree.openNodeScope(jjtn004); } try { /*@egen*/ shiftExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte004) { if (jjtc004) { jjtree.clearNodeScope(jjtn004); jjtc004 = false; } else { jjtree.popNode(); } if (jjte004 instanceof RuntimeException) { throw (RuntimeException)jjte004; } if (jjte004 instanceof ParseException) { throw (ParseException)jjte004; } throw (Error)jjte004; } finally { if (jjtc004) { jjtree.closeNodeScope(jjtn004, 2); } } /*@egen*/ | "in"/*@bgen(jjtree) #In( 2) */ { ASTIn jjtn005 = new ASTIn(JJTIN); boolean jjtc005 = true; jjtree.openNodeScope(jjtn005); } try { /*@egen*/ shiftExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte005) { if (jjtc005) { jjtree.clearNodeScope(jjtn005); jjtc005 = false; } else { jjtree.popNode(); } if (jjte005 instanceof RuntimeException) { throw (RuntimeException)jjte005; } if (jjte005 instanceof ParseException) { throw (ParseException)jjte005; } throw (Error)jjte005; } finally { if (jjtc005) { jjtree.closeNodeScope(jjtn005, 2); } } /*@egen*/ | "not" "in"/*@bgen(jjtree) #NotIn( 2) */ { ASTNotIn jjtn006 = new ASTNotIn(JJTNOTIN); boolean jjtc006 = true; jjtree.openNodeScope(jjtn006); } try { /*@egen*/ shiftExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte006) { if (jjtc006) { jjtree.clearNodeScope(jjtn006); jjtc006 = false; } else { jjtree.popNode(); } if (jjte006 instanceof RuntimeException) { throw (RuntimeException)jjte006; } if (jjte006 instanceof ParseException) { throw (ParseException)jjte006; } throw (Error)jjte006; } finally { if (jjtc006) { jjtree.closeNodeScope(jjtn006, 2); } } /*@egen*/ )* } // bit shift expressions (level 4) void shiftExpression() : {} { additiveExpression() ( ("<<" | "shl")/*@bgen(jjtree) #ShiftLeft( 2) */ { ASTShiftLeft jjtn001 = new ASTShiftLeft(JJTSHIFTLEFT); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ additiveExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ | (">>" | "shr")/*@bgen(jjtree) #ShiftRight( 2) */ { ASTShiftRight jjtn002 = new ASTShiftRight(JJTSHIFTRIGHT); boolean jjtc002 = true; jjtree.openNodeScope(jjtn002); } try { /*@egen*/ additiveExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte002) { if (jjtc002) { jjtree.clearNodeScope(jjtn002); jjtc002 = false; } else { jjtree.popNode(); } if (jjte002 instanceof RuntimeException) { throw (RuntimeException)jjte002; } if (jjte002 instanceof ParseException) { throw (ParseException)jjte002; } throw (Error)jjte002; } finally { if (jjtc002) { jjtree.closeNodeScope(jjtn002, 2); } } /*@egen*/ | (">>>" | "ushr")/*@bgen(jjtree) #UnsignedShiftRight( 2) */ { ASTUnsignedShiftRight jjtn003 = new ASTUnsignedShiftRight(JJTUNSIGNEDSHIFTRIGHT); boolean jjtc003 = true; jjtree.openNodeScope(jjtn003); } try { /*@egen*/ additiveExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte003) { if (jjtc003) { jjtree.clearNodeScope(jjtn003); jjtc003 = false; } else { jjtree.popNode(); } if (jjte003 instanceof RuntimeException) { throw (RuntimeException)jjte003; } if (jjte003 instanceof ParseException) { throw (ParseException)jjte003; } throw (Error)jjte003; } finally { if (jjtc003) { jjtree.closeNodeScope(jjtn003, 2); } } /*@egen*/ )* } // binary addition/subtraction (level 3) void additiveExpression() : {} { multiplicativeExpression() ( "+"/*@bgen(jjtree) #Add( 2) */ { ASTAdd jjtn001 = new ASTAdd(JJTADD); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ multiplicativeExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ | "-"/*@bgen(jjtree) #Subtract( 2) */ { ASTSubtract jjtn002 = new ASTSubtract(JJTSUBTRACT); boolean jjtc002 = true; jjtree.openNodeScope(jjtn002); } try { /*@egen*/ multiplicativeExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte002) { if (jjtc002) { jjtree.clearNodeScope(jjtn002); jjtc002 = false; } else { jjtree.popNode(); } if (jjte002 instanceof RuntimeException) { throw (RuntimeException)jjte002; } if (jjte002 instanceof ParseException) { throw (ParseException)jjte002; } throw (Error)jjte002; } finally { if (jjtc002) { jjtree.closeNodeScope(jjtn002, 2); } } /*@egen*/ )* } // multiplication/division/remainder (level 2) void multiplicativeExpression() : {} { unaryExpression() ( "*"/*@bgen(jjtree) #Multiply( 2) */ { ASTMultiply jjtn001 = new ASTMultiply(JJTMULTIPLY); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ unaryExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ | "/"/*@bgen(jjtree) #Divide( 2) */ { ASTDivide jjtn002 = new ASTDivide(JJTDIVIDE); boolean jjtc002 = true; jjtree.openNodeScope(jjtn002); } try { /*@egen*/ unaryExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte002) { if (jjtc002) { jjtree.clearNodeScope(jjtn002); jjtc002 = false; } else { jjtree.popNode(); } if (jjte002 instanceof RuntimeException) { throw (RuntimeException)jjte002; } if (jjte002 instanceof ParseException) { throw (ParseException)jjte002; } throw (Error)jjte002; } finally { if (jjtc002) { jjtree.closeNodeScope(jjtn002, 2); } } /*@egen*/ | "%"/*@bgen(jjtree) #Remainder( 2) */ { ASTRemainder jjtn003 = new ASTRemainder(JJTREMAINDER); boolean jjtc003 = true; jjtree.openNodeScope(jjtn003); } try { /*@egen*/ unaryExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte003) { if (jjtc003) { jjtree.clearNodeScope(jjtn003); jjtc003 = false; } else { jjtree.popNode(); } if (jjte003 instanceof RuntimeException) { throw (RuntimeException)jjte003; } if (jjte003 instanceof ParseException) { throw (ParseException)jjte003; } throw (Error)jjte003; } finally { if (jjtc003) { jjtree.closeNodeScope(jjtn003, 2); } } /*@egen*/ )* } // unary (level 1) void unaryExpression() : { StringBuffer sb; Token t; ASTInstanceof ionode; } { ( "-"/*@bgen(jjtree) #Negate( 1) */ { ASTNegate jjtn001 = new ASTNegate(JJTNEGATE); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ unaryExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 1); } } /*@egen*/ | "+" unaryExpression() // Just leave it there | "~"/*@bgen(jjtree) #BitNegate( 1) */ { ASTBitNegate jjtn002 = new ASTBitNegate(JJTBITNEGATE); boolean jjtc002 = true; jjtree.openNodeScope(jjtn002); } try { /*@egen*/ unaryExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte002) { if (jjtc002) { jjtree.clearNodeScope(jjtn002); jjtc002 = false; } else { jjtree.popNode(); } if (jjte002 instanceof RuntimeException) { throw (RuntimeException)jjte002; } if (jjte002 instanceof ParseException) { throw (ParseException)jjte002; } throw (Error)jjte002; } finally { if (jjtc002) { jjtree.closeNodeScope(jjtn002, 1); } } /*@egen*/ | ("!" | "not")/*@bgen(jjtree) #Not( 1) */ { ASTNot jjtn003 = new ASTNot(JJTNOT); boolean jjtc003 = true; jjtree.openNodeScope(jjtn003); } try { /*@egen*/ unaryExpression()/*@bgen(jjtree)*/ } catch (Throwable jjte003) { if (jjtc003) { jjtree.clearNodeScope(jjtn003); jjtc003 = false; } else { jjtree.popNode(); } if (jjte003 instanceof RuntimeException) { throw (RuntimeException)jjte003; } if (jjte003 instanceof ParseException) { throw (ParseException)jjte003; } throw (Error)jjte003; } finally { if (jjtc003) { jjtree.closeNodeScope(jjtn003, 1); } } /*@egen*/ | navigationChain() [ "instanceof" t = classNamePart()/*@bgen(jjtree) #Instanceof( 1) */ { ASTInstanceof jjtn004 = new ASTInstanceof(JJTINSTANCEOF); boolean jjtc004 = true; jjtree.openNodeScope(jjtn004); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn004, 1); jjtc004 = false; } /*@egen*/ { sb = new StringBuffer(t.image); ionode = jjtn004; }/*@bgen(jjtree)*/ } finally { if (jjtc004) { jjtree.closeNodeScope(jjtn004, 1); } } /*@egen*/ ( "." t = classNamePart() { sb.append('.').append( t.image ); } )* { ionode.setTargetType( new String(sb) ); } ] ) } // navigation chain: property references, method calls, projections, selections, etc. // Supports null-safe navigation with the ?. operator void navigationChain() : { boolean nullSafe = false; } { primaryExpression() ( ( { nullSafe = true; } | "." { nullSafe = false; } )/*@bgen(jjtree) #Chain( 2) */ { ASTChain jjtn001 = new ASTChain(JJTCHAIN); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ ( /* Prevent the "eval" ambiguity from issuing a warning; see discussion below. */ ( LOOKAHEAD(2) methodCall() | propertyName() ) // Also handle "{", which requires a lookahead of 2. | ( LOOKAHEAD(2) projection() | selection() ) | "(" expression() ")" )/*@bgen(jjtree)*/ { jjtn001.setNullSafe(nullSafe); } } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 2); } } /*@egen*/ |/*@bgen(jjtree) #Chain( 2) */ { ASTChain jjtn002 = new ASTChain(JJTCHAIN); boolean jjtc002 = true; jjtree.openNodeScope(jjtn002); } try { /*@egen*/ index()/*@bgen(jjtree)*/ } catch (Throwable jjte002) { if (jjtc002) { jjtree.clearNodeScope(jjtn002); jjtc002 = false; } else { jjtree.popNode(); } if (jjte002 instanceof RuntimeException) { throw (RuntimeException)jjte002; } if (jjte002 instanceof ParseException) { throw (ParseException)jjte002; } throw (Error)jjte002; } finally { if (jjtc002) { jjtree.closeNodeScope(jjtn002, 2); } } /*@egen*/ | "(" expression()/*@bgen(jjtree) #Eval( 2) */ { ASTEval jjtn003 = new ASTEval(JJTEVAL); boolean jjtc003 = true; jjtree.openNodeScope(jjtn003); } try { /*@egen*/ ")"/*@bgen(jjtree)*/ } finally { if (jjtc003) { jjtree.closeNodeScope(jjtn003, 2); } } /*@egen*/ /* Using parentheses to indicate evaluation of the current object makes this language ambiguous, because the expression "ident(args)" could be seen as a single method call or as a property name followed by an evaluation. We always put the method call first and turn off the ambiguity warning; we always want to interpret this as a method call. */ )* } void primaryExpression() : { Token t; String className = null; } { ( ( | | | | )/*@bgen(jjtree) #Const( 0) */ { ASTConst jjtn001 = new ASTConst(JJTCONST); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn001, 0); jjtc001 = false; } /*@egen*/ { jjtn001.setValue( token_source.literalValue ); }/*@bgen(jjtree)*/ } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 0); } } /*@egen*/ | "true"/*@bgen(jjtree) #Const( 0) */ { ASTConst jjtn002 = new ASTConst(JJTCONST); boolean jjtc002 = true; jjtree.openNodeScope(jjtn002); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn002, 0); jjtc002 = false; } /*@egen*/ { jjtn002.setValue( Boolean.TRUE ); }/*@bgen(jjtree)*/ } finally { if (jjtc002) { jjtree.closeNodeScope(jjtn002, 0); } } /*@egen*/ | "false"/*@bgen(jjtree) #Const( 0) */ { ASTConst jjtn003 = new ASTConst(JJTCONST); boolean jjtc003 = true; jjtree.openNodeScope(jjtn003); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn003, 0); jjtc003 = false; } /*@egen*/ { jjtn003.setValue( Boolean.FALSE ); }/*@bgen(jjtree)*/ } finally { if (jjtc003) { jjtree.closeNodeScope(jjtn003, 0); } } /*@egen*/ |/*@bgen(jjtree) #Const( 0) */ { ASTConst jjtn004 = new ASTConst(JJTCONST); boolean jjtc004 = true; jjtree.openNodeScope(jjtn004); } try { /*@egen*/ "null"/*@bgen(jjtree)*/ } finally { if (jjtc004) { jjtree.closeNodeScope(jjtn004, 0); } } /*@egen*/ // Null is the default value in an ASTConst | LOOKAHEAD(2) "#this"/*@bgen(jjtree) #ThisVarRef( 0) */ { ASTThisVarRef jjtn005 = new ASTThisVarRef(JJTTHISVARREF); boolean jjtc005 = true; jjtree.openNodeScope(jjtn005); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn005, 0); jjtc005 = false; } /*@egen*/ { jjtn005.setName( "this" ); }/*@bgen(jjtree)*/ } finally { if (jjtc005) { jjtree.closeNodeScope(jjtn005, 0); } } /*@egen*/ | LOOKAHEAD(2) "#root"/*@bgen(jjtree) #RootVarRef( 0) */ { ASTRootVarRef jjtn006 = new ASTRootVarRef(JJTROOTVARREF); boolean jjtc006 = true; jjtree.openNodeScope(jjtn006); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn006, 0); jjtc006 = false; } /*@egen*/ { jjtn006.setName( "root" ); }/*@bgen(jjtree)*/ } finally { if (jjtc006) { jjtree.closeNodeScope(jjtn006, 0); } } /*@egen*/ | LOOKAHEAD(2) "#" t=/*@bgen(jjtree) #VarRef( 0) */ { ASTVarRef jjtn007 = new ASTVarRef(JJTVARREF); boolean jjtc007 = true; jjtree.openNodeScope(jjtn007); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn007, 0); jjtc007 = false; } /*@egen*/ { jjtn007.setName( t.image ); }/*@bgen(jjtree)*/ } finally { if (jjtc007) { jjtree.closeNodeScope(jjtn007, 0); } } /*@egen*/ | LOOKAHEAD(2) ":" "[" expression() "]"/*@bgen(jjtree) #Const( 1) */ { ASTConst jjtn008 = new ASTConst(JJTCONST); boolean jjtc008 = true; jjtree.openNodeScope(jjtn008); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn008, 1); jjtc008 = false; } /*@egen*/ { jjtn008.setValue( jjtn008.jjtGetChild(0) ); }/*@bgen(jjtree)*/ } finally { if (jjtc008) { jjtree.closeNodeScope(jjtn008, 1); } } /*@egen*/ | staticReference() | LOOKAHEAD(2) constructorCall() | // Prevent the "eval" ambiguity from issuing a warning; see discussion elsewhere. ( LOOKAHEAD(2) methodCall() | propertyName() ) | index() | "(" expression() ")" | "{"/*@bgen(jjtree) List */ { ASTList jjtn009 = new ASTList(JJTLIST); boolean jjtc009 = true; jjtree.openNodeScope(jjtn009); } try { /*@egen*/ [assignmentExpression() ("," assignmentExpression())*]/*@bgen(jjtree)*/ } catch (Throwable jjte009) { if (jjtc009) { jjtree.clearNodeScope(jjtn009); jjtc009 = false; } else { jjtree.popNode(); } if (jjte009 instanceof RuntimeException) { throw (RuntimeException)jjte009; } if (jjte009 instanceof ParseException) { throw (ParseException)jjte009; } throw (Error)jjte009; } finally { if (jjtc009) { jjtree.closeNodeScope(jjtn009, true); } } /*@egen*/ "}" | LOOKAHEAD(2)/*@bgen(jjtree) Map */ { ASTMap jjtn010 = new ASTMap(JJTMAP); boolean jjtc010 = true; jjtree.openNodeScope(jjtn010); } try { /*@egen*/ ( "#" (className=classReference())? "{" [keyValueExpression() ("," keyValueExpression())*] { jjtn010.setClassName(className); } "}" )/*@bgen(jjtree)*/ } catch (Throwable jjte010) { if (jjtc010) { jjtree.clearNodeScope(jjtn010); jjtc010 = false; } else { jjtree.popNode(); } if (jjte010 instanceof RuntimeException) { throw (RuntimeException)jjte010; } if (jjte010 instanceof ParseException) { throw (ParseException)jjte010; } throw (Error)jjte010; } finally { if (jjtc010) { jjtree.closeNodeScope(jjtn010, true); } } /*@egen*/ ) } void keyValueExpression() : {} {/*@bgen(jjtree) KeyValue */ { ASTKeyValue jjtn001 = new ASTKeyValue(JJTKEYVALUE); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ ( assignmentExpression() (":" assignmentExpression())? )/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, true); } } /*@egen*/ } void staticReference() : { String className = "java.lang.Math"; Token t; } { className=classReference() ( // Prevent the "eval" ambiguity from issuing a warning; see discussion elsewhere. LOOKAHEAD(2) staticMethodCall( className ) | t=/*@bgen(jjtree) #StaticField( 0) */ { ASTStaticField jjtn001 = new ASTStaticField(JJTSTATICFIELD); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn001, 0); jjtc001 = false; } /*@egen*/ { jjtn001.init( className, t.image ); }/*@bgen(jjtree)*/ } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 0); } } /*@egen*/ ) } String classReference(): { String result = "java.lang.Math"; } { "@" ( result=className() )? "@" { return result; } } String className(): { Token t; StringBuffer result; } { t=classNamePart() { result = new StringBuffer( t.image ); } ( "." t=classNamePart() { result.append('.').append( t.image ); } )* { return new String(result); } } /** * Helper production to match class name parts, which can be either identifiers * or reserved keywords (like "or", "and", "not", etc.) that appear in package names. * This fixes Issue #103 where package names containing keywords would fail to parse. */ Token classNamePart(): { Token t; } { ( t= | "or" { t = token; } | "and" { t = token; } | "not" { t = token; } | "in" { t = token; } | "bor" { t = token; } | "xor" { t = token; } | "band" { t = token; } | "eq" { t = token; } | "neq" { t = token; } | "lt" { t = token; } | "lte" { t = token; } | "gt" { t = token; } | "gte" { t = token; } | "shl" { t = token; } | "shr" { t = token; } | "ushr" { t = token; } | "new" { t = token; } | "true" { t = token; } | "false" { t = token; } | "null" { t = token; } | "instanceof" { t = token; } ) { return t; } } void constructorCall() : {/*@bgen(jjtree) Ctor */ ASTCtor jjtn000 = new ASTCtor(JJTCTOR); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/ String className; Token t; StringBuffer sb; } {/*@bgen(jjtree) Ctor */ try { /*@egen*/ "new" className=className() ( LOOKAHEAD(2) ( "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")"/*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn000, true); jjtc000 = false; } /*@egen*/ { jjtn000.setClassName(className); } ) | LOOKAHEAD(2) ( "[" "]" "{"/*@bgen(jjtree) List */ { ASTList jjtn001 = new ASTList(JJTLIST); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*/ [assignmentExpression() ("," assignmentExpression())*]/*@bgen(jjtree)*/ } catch (Throwable jjte001) { if (jjtc001) { jjtree.clearNodeScope(jjtn001); jjtc001 = false; } else { jjtree.popNode(); } if (jjte001 instanceof RuntimeException) { throw (RuntimeException)jjte001; } if (jjte001 instanceof ParseException) { throw (ParseException)jjte001; } throw (Error)jjte001; } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, true); } } /*@egen*/ "}"/*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn000, true); jjtc000 = false; } /*@egen*/ { jjtn000.setClassName(className); jjtn000.setArray(true); } ) | LOOKAHEAD(2) ( "[" assignmentExpression() "]"/*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn000, true); jjtc000 = false; } /*@egen*/ { jjtn000.setClassName(className); jjtn000.setArray(true); } ) )/*@bgen(jjtree)*/ } catch (Throwable jjte000) { if (jjtc000) { jjtree.clearNodeScope(jjtn000); jjtc000 = false; } else { jjtree.popNode(); } if (jjte000 instanceof RuntimeException) { throw (RuntimeException)jjte000; } if (jjte000 instanceof ParseException) { throw (ParseException)jjte000; } throw (Error)jjte000; } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } void propertyName() : {/*@bgen(jjtree) Property */ ASTProperty jjtn000 = new ASTProperty(JJTPROPERTY); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/ Token t; } {/*@bgen(jjtree) Property */ try { /*@egen*/ t=/*@bgen(jjtree) Const */ { ASTConst jjtn001 = new ASTConst(JJTCONST); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn001, true); jjtc001 = false; } /*@egen*/ { jjtn001.setValue( t.image ); }/*@bgen(jjtree)*/ } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, true); } } /*@egen*/ /*@bgen(jjtree)*/ } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } void staticMethodCall( String className ) : {/*@bgen(jjtree) StaticMethod */ ASTStaticMethod jjtn000 = new ASTStaticMethod(JJTSTATICMETHOD); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/ Token t; } {/*@bgen(jjtree) StaticMethod */ try { /*@egen*/ t= "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")"/*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn000, true); jjtc000 = false; } /*@egen*/ { jjtn000.init( className, t.image ); }/*@bgen(jjtree)*/ } catch (Throwable jjte000) { if (jjtc000) { jjtree.clearNodeScope(jjtn000); jjtc000 = false; } else { jjtree.popNode(); } if (jjte000 instanceof RuntimeException) { throw (RuntimeException)jjte000; } if (jjte000 instanceof ParseException) { throw (ParseException)jjte000; } throw (Error)jjte000; } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } void methodCall() : {/*@bgen(jjtree) Method */ ASTMethod jjtn000 = new ASTMethod(JJTMETHOD); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/ Token t; } {/*@bgen(jjtree) Method */ try { /*@egen*/ t= "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")"/*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn000, true); jjtc000 = false; } /*@egen*/ { jjtn000.setMethodName( t.image ); }/*@bgen(jjtree)*/ } catch (Throwable jjte000) { if (jjtc000) { jjtree.clearNodeScope(jjtn000); jjtc000 = false; } else { jjtree.popNode(); } if (jjte000 instanceof RuntimeException) { throw (RuntimeException)jjte000; } if (jjte000 instanceof ParseException) { throw (ParseException)jjte000; } throw (Error)jjte000; } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } /** * Apply an expression to all elements of a collection, creating a new collection * as the result. */ void projection() : {/*@bgen(jjtree) Project */ ASTProject jjtn000 = new ASTProject(JJTPROJECT); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/} {/*@bgen(jjtree) Project */ try { /*@egen*/ "{" expression() "}"/*@bgen(jjtree)*/ } catch (Throwable jjte000) { if (jjtc000) { jjtree.clearNodeScope(jjtn000); jjtc000 = false; } else { jjtree.popNode(); } if (jjte000 instanceof RuntimeException) { throw (RuntimeException)jjte000; } if (jjte000 instanceof ParseException) { throw (ParseException)jjte000; } throw (Error)jjte000; } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } void selection() : {} { LOOKAHEAD(2) selectAll() | LOOKAHEAD(2) selectFirst() | LOOKAHEAD(2) selectLast() } /** * Apply a boolean expression to all elements of a collection, creating a new collection * containing those elements for which the expression returned true. */ void selectAll() : {/*@bgen(jjtree) Select */ ASTSelect jjtn000 = new ASTSelect(JJTSELECT); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/} {/*@bgen(jjtree) Select */ try { /*@egen*/ "{" "?" expression() "}"/*@bgen(jjtree)*/ } catch (Throwable jjte000) { if (jjtc000) { jjtree.clearNodeScope(jjtn000); jjtc000 = false; } else { jjtree.popNode(); } if (jjte000 instanceof RuntimeException) { throw (RuntimeException)jjte000; } if (jjte000 instanceof ParseException) { throw (ParseException)jjte000; } throw (Error)jjte000; } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } /** * Apply a boolean expression to all elements of a collection, creating a new collection * containing those elements for the first element for which the expression returned true. */ void selectFirst() : {/*@bgen(jjtree) SelectFirst */ ASTSelectFirst jjtn000 = new ASTSelectFirst(JJTSELECTFIRST); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/} {/*@bgen(jjtree) SelectFirst */ try { /*@egen*/ "{" "^" expression() "}"/*@bgen(jjtree)*/ } catch (Throwable jjte000) { if (jjtc000) { jjtree.clearNodeScope(jjtn000); jjtc000 = false; } else { jjtree.popNode(); } if (jjte000 instanceof RuntimeException) { throw (RuntimeException)jjte000; } if (jjte000 instanceof ParseException) { throw (ParseException)jjte000; } throw (Error)jjte000; } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } /** * Apply a boolean expression to all elements of a collection, creating a new collection * containing those elements for the first element for which the expression returned true. */ void selectLast() : {/*@bgen(jjtree) SelectLast */ ASTSelectLast jjtn000 = new ASTSelectLast(JJTSELECTLAST); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/} {/*@bgen(jjtree) SelectLast */ try { /*@egen*/ "{" "$" expression() "}"/*@bgen(jjtree)*/ } catch (Throwable jjte000) { if (jjtc000) { jjtree.clearNodeScope(jjtn000); jjtc000 = false; } else { jjtree.popNode(); } if (jjte000 instanceof RuntimeException) { throw (RuntimeException)jjte000; } if (jjte000 instanceof ParseException) { throw (ParseException)jjte000; } throw (Error)jjte000; } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } void index() : {/*@bgen(jjtree) Property */ ASTProperty jjtn000 = new ASTProperty(JJTPROPERTY); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); /*@egen*/} {/*@bgen(jjtree) Property */ try { /*@egen*/ "[" expression() "]"/*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn000, true); jjtc000 = false; } /*@egen*/ { jjtn000.setIndexedAccess(true); } | /*@bgen(jjtree) Const */ { ASTConst jjtn001 = new ASTConst(JJTCONST); boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); } try { /*@egen*//*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn001, true); jjtc001 = false; } /*@egen*/ { jjtn001.setValue( token_source.literalValue ); }/*@bgen(jjtree)*/ } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, true); } } /*@egen*/ /*@bgen(jjtree)*/ { jjtree.closeNodeScope(jjtn000, true); jjtc000 = false; } /*@egen*/ { jjtn000.setIndexedAccess(true); }/*@bgen(jjtree)*/ } catch (Throwable jjte000) { if (jjtc000) { jjtree.clearNodeScope(jjtn000); jjtc000 = false; } else { jjtree.popNode(); } if (jjte000 instanceof RuntimeException) { throw (RuntimeException)jjte000; } if (jjte000 instanceof ParseException) { throw (ParseException)jjte000; } throw (Error)jjte000; } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); } } /*@egen*/ } // LEXER PRODUCTIONS TOKEN_MGR_DECLS: { /** Holds the last value computed by a constant token. */ Object literalValue; /** Holds the last character escaped or in a character literal. */ private char charValue; /** Holds char literal start token. */ private char charLiteralStartQuote; /** Holds the last string literal parsed. */ private StringBuffer stringBuffer; /** Converts an escape sequence into a character value. */ private char escapeChar() { int ofs = image.length() - 1; switch ( image.charAt(ofs) ) { case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; case 'b': return '\b'; case 'f': return '\f'; case '\\': return '\\'; case '\'': return '\''; case '\"': return '\"'; } // Otherwise, it's an octal number. Find the backslash and convert. while ( image.charAt(--ofs) != '\\' ) {} int value = 0; while ( ++ofs < image.length() ) value = (value << 3) | (image.charAt(ofs) - '0'); return (char) value; } private Object makeInt() { Object result; String s = image.toString(); int base = 10; if ( s.charAt(0) == '0' ) base = (s.length() > 1 && (s.charAt(1) == 'x' || s.charAt(1) == 'X'))? 16 : 8; if ( base == 16 ) s = s.substring(2); // Trim the 0x off the front switch ( s.charAt(s.length()-1) ) { case 'l': case 'L': result = Long.valueOf( s.substring(0,s.length()-1), base ); break; case 'h': case 'H': result = new BigInteger( s.substring(0,s.length()-1), base ); break; default: result = Integer.valueOf( s, base ); break; } return result; } private Object makeFloat() { String s = image.toString(); switch ( s.charAt(s.length()-1) ) { case 'f': case 'F': return Float.valueOf( s ); case 'b': case 'B': return new BigDecimal( s.substring(0,s.length()-1) ); case 'd': case 'D': default: return Double.valueOf( s ); } } } // Whitespace -- ignored SKIP: { " " | "\t" | "\f" | "\r" | "\n" } // Null-safe navigation operator (must be defined before IDENT to ensure proper tokenization) TOKEN: { < SAFE_DOT: "?." > } // An identifier. TOKEN: { < IDENT: (|)* > | < #LETTER: [ "$", "A"-"Z", "_", "a"-"z", "\u00c0"-"\u00d6", "\u00d8"-"\u00f6", "\u00f8"-"\u00ff", "\u0100"-"\u1fff", "\u3040"-"\u318f", "\u3300"-"\u337f", "\u3400"-"\u3d2d", "\u4e00"-"\u9fff", "\uf900"-"\ufaff" ] > | < #DIGIT: [ "0"-"9", "\u0660"-"\u0669", "\u06f0"-"\u06f9", "\u0966"-"\u096f", "\u09e6"-"\u09ef", "\u0a66"-"\u0a6f", "\u0ae6"-"\u0aef", "\u0b66"-"\u0b6f", "\u0be7"-"\u0bef", "\u0c66"-"\u0c6f", "\u0ce6"-"\u0cef", "\u0d66"-"\u0d6f", "\u0e50"-"\u0e59", "\u0ed0"-"\u0ed9", "\u1040"-"\u1049" ] > } /** * Token for "dynamic subscripts", which are one of: [^], [|], [$], and [*]. The * appropriate constant from the DynamicSubscript class is stored in the token manager's * "value" field. */ TOKEN: { < DYNAMIC_SUBSCRIPT: "[" ["^","|","$","*"] "]" > { switch (image.charAt(1)) { case '^': literalValue = DynamicSubscript.first; break; case '|': literalValue = DynamicSubscript.mid; break; case '$': literalValue = DynamicSubscript.last; break; case '*': literalValue = DynamicSubscript.all; break; } } } /** * Character and string literals, whose object value is stored in the token manager's * "literalValue" field. */ MORE: { "`" : WithinBackCharLiteral | "'" { stringBuffer = new StringBuffer(); }: WithinCharLiteral | "\"" { stringBuffer = new StringBuffer(); }: WithinStringLiteral } MORE: { < ESC: "\\" ( ["n","r","t","b","f","\\","'","`","\""] | (["0"-"3"])? ["0"-"7"] (["0"-"7"])? ) > { charValue = escapeChar(); stringBuffer.append(charValue); } | < (~["'","\\"]) > { charValue = image.charAt( image.length()-1 ); stringBuffer.append(charValue); } } TOKEN: { < CHAR_LITERAL: "'"> { if (stringBuffer.length() == 1) { literalValue = charValue; } else { literalValue = new String( stringBuffer ); } } : DEFAULT } MORE: { < BACK_CHAR_ESC: > { charValue = escapeChar(); } | < (~["`","\\"]) > { charValue = image.charAt( image.length()-1 ); } } TOKEN: { < BACK_CHAR_LITERAL: "`"> { literalValue = charValue; }: DEFAULT } MORE: { < STRING_ESC: > { stringBuffer.append( escapeChar() ); } | < (~["\"","\\"]) > { stringBuffer.append( image.charAt(image.length()-1) ); } } TOKEN: { { literalValue = new String( stringBuffer ); } : DEFAULT } /** * Integer or real Numeric literal, whose object value is stored in the token manager's * "literalValue" field. */ TOKEN: { < INT_LITERAL: ( "0" (["0"-"7"])* | ["1"-"9"] (["0"-"9"])* | "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ ) (["l","L","h","H"])? > { literalValue = makeInt(); } | < FLT_LITERAL: ( ()? ()? | ()? | ) > { literalValue = makeFloat(); } | < #DEC_FLT: (["0"-"9"])+ "." (["0"-"9"])* | "." (["0"-"9"])+ > | < #DEC_DIGITS: (["0"-"9"])+ > | < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > | < #FLT_SUFF: ["d","D","f","F","b","B"] > } ================================================ FILE: ognl/src/main/jjtree/ognl.jjt ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT 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 file defines the syntax of OGNL, the Object-Graph Navigation Language. This * language was devised by Drew Davidson, who called it Key-Value Coding Language. Luke * Blanshard then made up the new name and reimplemented it using ANTLR, refining and * polishing the language a bit on the way. Drew maintained the system for a couple of * years; then Luke converted the ANTLR grammar to JavaCC, to eliminate the run-time * dependency on ANTLR. * * See package.html for a description of the language. */ options { // Parser options LOOKAHEAD = 1; STATIC = false; JAVA_UNICODE_ESCAPE = true; UNICODE_INPUT = true; // Tree options MULTI = true; NODE_DEFAULT_VOID = true; } PARSER_BEGIN(OgnlParser) package ognl; import java.math.*; /** * OgnlParser is a JavaCC parser class; it translates OGNL expressions into abstract * syntax trees (ASTs) that can then be interpreted by the getValue and setValue methods. */ public class OgnlParser { } PARSER_END(OgnlParser) /** * This is the top-level construct of OGNL. */ Node topLevelExpression() : {} { expression() { return jjtree.rootNode(); } } // sequence (level 14) void expression() : {} { assignmentExpression() ( "," assignmentExpression() #Sequence(2) )* } // assignment expression (level 13) void assignmentExpression() : {} { conditionalTestExpression() [ "=" assignmentExpression() #Assign(2) ] } // conditional test (level 12) void conditionalTestExpression() : {} { logicalOrExpression() [ "?" conditionalTestExpression() ":" conditionalTestExpression() #Test(3) ] } // logical or (||) (level 11) void logicalOrExpression() : {} { logicalAndExpression() (("||" | "or") logicalAndExpression() #Or(2) )* } // logical and (&&) (level 10) void logicalAndExpression() : {} { inclusiveOrExpression() (("&&" | "and") inclusiveOrExpression() #And(2) )* } // bitwise or non-short-circuiting or (|) (level 9) void inclusiveOrExpression() : {} { exclusiveOrExpression() (("|" | "bor") exclusiveOrExpression() #BitOr(2) )* } // exclusive or (^) (level 8) void exclusiveOrExpression() : {} { andExpression() (("^" | "xor") andExpression() #Xor(2) )* } // bitwise or non-short-circuiting and (&) (level 7) void andExpression() : {} { equalityExpression() (("&" | "band") equalityExpression() #BitAnd(2) )* } // equality/inequality (==/!=) (level 6) void equalityExpression() : {} { relationalExpression() ( ("==" | "eq") relationalExpression() #Eq(2) | ("!=" | "neq") relationalExpression() #NotEq(2) )* } // boolean relational expressions (level 5) void relationalExpression() : {} { shiftExpression() ( ("<" | "lt") shiftExpression() #Less(2) | (">" | "gt") shiftExpression() #Greater(2) | ("<=" | "lte") shiftExpression() #LessEq(2) | (">=" | "gte") shiftExpression() #GreaterEq(2) | "in" shiftExpression() #In(2) | "not" "in" shiftExpression() #NotIn(2) )* } // bit shift expressions (level 4) void shiftExpression() : {} { additiveExpression() ( ("<<" | "shl") additiveExpression() #ShiftLeft(2) | (">>" | "shr") additiveExpression() #ShiftRight(2) | (">>>" | "ushr") additiveExpression() #UnsignedShiftRight(2) )* } // binary addition/subtraction (level 3) void additiveExpression() : {} { multiplicativeExpression() ( "+" multiplicativeExpression() #Add(2) | "-" multiplicativeExpression() #Subtract(2) )* } // multiplication/division/remainder (level 2) void multiplicativeExpression() : {} { unaryExpression() ( "*" unaryExpression() #Multiply(2) | "/" unaryExpression() #Divide(2) | "%" unaryExpression() #Remainder(2) )* } // unary (level 1) void unaryExpression() : { StringBuffer sb; Token t; ASTInstanceof ionode; } { ( "-" unaryExpression() #Negate(1) | "+" unaryExpression() // Just leave it there | "~" unaryExpression() #BitNegate(1) | ("!" | "not") unaryExpression() #Not(1) | navigationChain() [ "instanceof" t = { sb = new StringBuffer(t.image); ionode = jjtThis; } #Instanceof(1) ( "." t = { sb.append('.').append( t.image ); } )* { ionode.setTargetType( new String(sb) ); } ] ) } // navigation chain: property references, method calls, projections, selections, etc. void navigationChain() : {} { primaryExpression() ( "." ( /* Prevent the "eval" ambiguity from issuing a warning; see discussion below. */ ( LOOKAHEAD(2) methodCall() | propertyName() ) // Also handle "{", which requires a lookahead of 2. | ( LOOKAHEAD(2) projection() | selection() ) | "(" expression() ")" ) #Chain(2) | index() #Chain(2) | "(" expression() ")" #Eval(2) /* Using parentheses to indicate evaluation of the current object makes this language ambiguous, because the expression "ident(args)" could be seen as a single method call or as a property name followed by an evaluation. We always put the method call first and turn off the ambiguity warning; we always want to interpret this as a method call. */ )* } void primaryExpression() : { Token t; String className = null; } { ( ( | | | | ) { jjtThis.setValue( token_source.literalValue ); } #Const(0) | "true" { jjtThis.setValue( Boolean.TRUE ); } #Const(0) | "false" { jjtThis.setValue( Boolean.FALSE ); } #Const(0) | "null" #Const(0) // Null is the default value in an ASTConst | LOOKAHEAD(2) "#this" { jjtThis.setName( "this" ); } #ThisVarRef(0) | LOOKAHEAD(2) "#root" { jjtThis.setName( "root" ); } #RootVarRef(0) | LOOKAHEAD(2) "#" t= { jjtThis.setName( t.image ); } #VarRef(0) | LOOKAHEAD(2) ":" "[" expression() "]" { jjtThis.setValue( jjtThis.jjtGetChild(0) ); } #Const(1) | staticReference() | LOOKAHEAD(2) constructorCall() | // Prevent the "eval" ambiguity from issuing a warning; see discussion elsewhere. ( LOOKAHEAD(2) methodCall() | propertyName() ) | index() | "(" expression() ")" | "{" [assignmentExpression() ("," assignmentExpression())*] #List "}" | LOOKAHEAD(2) ( "#" (className=classReference())? "{" [keyValueExpression() ("," keyValueExpression())*] { jjtThis.setClassName(className); } "}" ) #Map ) } void keyValueExpression() : {} { ( assignmentExpression() (":" assignmentExpression())? ) #KeyValue } void staticReference() : { String className = "java.lang.Math"; Token t; } { className=classReference() ( // Prevent the "eval" ambiguity from issuing a warning; see discussion elsewhere. LOOKAHEAD(2) staticMethodCall( className ) | t= { jjtThis.init( className, t.image ); } #StaticField(0) ) } String classReference(): { String result = "java.lang.Math"; } { "@" ( result=className() )? "@" { return result; } } String className(): { Token t; StringBuffer result; } { t= { result = new StringBuffer( t.image ); } ( "." t= { result.append('.').append( t.image ); } )* { return new String(result); } } void constructorCall() #Ctor : { String className; Token t; StringBuffer sb; } { "new" className=className() ( LOOKAHEAD(2) ( "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")" { jjtThis.setClassName(className); } ) | LOOKAHEAD(2) ( "[" "]" "{" [assignmentExpression() ("," assignmentExpression())*] #List "}" { jjtThis.setClassName(className); jjtThis.setArray(true); } ) | LOOKAHEAD(2) ( "[" assignmentExpression() "]" { jjtThis.setClassName(className); jjtThis.setArray(true); } ) ) } void propertyName() #Property : { Token t; } { t= { jjtThis.setValue( t.image ); } #Const } void staticMethodCall( String className ) #StaticMethod : { Token t; } { t= "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")" { jjtThis.init( className, t.image ); } } void methodCall() #Method : { Token t; } { t= "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")" { jjtThis.setMethodName( t.image ); } } /** * Apply an expression to all elements of a collection, creating a new collection * as the result. */ void projection() #Project : {} { "{" expression() "}" } void selection() : {} { LOOKAHEAD(2) selectAll() | LOOKAHEAD(2) selectFirst() | LOOKAHEAD(2) selectLast() } /** * Apply a boolean expression to all elements of a collection, creating a new collection * containing those elements for which the expression returned true. */ void selectAll() #Select : {} { "{" "?" expression() "}" } /** * Apply a boolean expression to all elements of a collection, creating a new collection * containing those elements for the first element for which the expression returned true. */ void selectFirst() #SelectFirst : {} { "{" "^" expression() "}" } /** * Apply a boolean expression to all elements of a collection, creating a new collection * containing those elements for the first element for which the expression returned true. */ void selectLast() #SelectLast : {} { "{" "$" expression() "}" } void index() #Property : {} { "[" expression() "]" { jjtThis.setIndexedAccess(true); } | { jjtThis.setValue( token_source.literalValue ); } #Const { jjtThis.setIndexedAccess(true); } } // LEXER PRODUCTIONS TOKEN_MGR_DECLS: { /** Holds the last value computed by a constant token. */ Object literalValue; /** Holds the last character escaped or in a character literal. */ private char charValue; /** Holds char literal start token. */ private char charLiteralStartQuote; /** Holds the last string literal parsed. */ private StringBuffer stringBuffer; /** Converts an escape sequence into a character value. */ private char escapeChar() { int ofs = image.length() - 1; switch ( image.charAt(ofs) ) { case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; case 'b': return '\b'; case 'f': return '\f'; case '\\': return '\\'; case '\'': return '\''; case '\"': return '\"'; } // Otherwise, it's an octal number. Find the backslash and convert. while ( image.charAt(--ofs) != '\\' ) {} int value = 0; while ( ++ofs < image.length() ) value = (value << 3) | (image.charAt(ofs) - '0'); return (char) value; } private Object makeInt() { Object result; String s = image.toString(); int base = 10; if ( s.charAt(0) == '0' ) base = (s.length() > 1 && (s.charAt(1) == 'x' || s.charAt(1) == 'X'))? 16 : 8; if ( base == 16 ) s = s.substring(2); // Trim the 0x off the front switch ( s.charAt(s.length()-1) ) { case 'l': case 'L': result = Long.valueOf( s.substring(0,s.length()-1), base ); break; case 'h': case 'H': result = new BigInteger( s.substring(0,s.length()-1), base ); break; default: result = Integer.valueOf( s, base ); break; } return result; } private Object makeFloat() { String s = image.toString(); switch ( s.charAt(s.length()-1) ) { case 'f': case 'F': return Float.valueOf( s ); case 'b': case 'B': return new BigDecimal( s.substring(0,s.length()-1) ); case 'd': case 'D': default: return Double.valueOf( s ); } } } // Whitespace -- ignored SKIP: { " " | "\t" | "\f" | "\r" | "\n" } // An identifier. TOKEN: { < IDENT: (|)* > | < #LETTER: [ "\u0024", "\u0041"-"\u005a", "\u005f", "\u0061"-"\u007a", "\u00c0"-"\u00d6", "\u00d8"-"\u00f6", "\u00f8"-"\u00ff", "\u0100"-"\u1fff", "\u3040"-"\u318f", "\u3300"-"\u337f", "\u3400"-"\u3d2d", "\u4e00"-"\u9fff", "\uf900"-"\ufaff" ] > | < #DIGIT: [ "\u0030"-"\u0039", "\u0660"-"\u0669", "\u06f0"-"\u06f9", "\u0966"-"\u096f", "\u09e6"-"\u09ef", "\u0a66"-"\u0a6f", "\u0ae6"-"\u0aef", "\u0b66"-"\u0b6f", "\u0be7"-"\u0bef", "\u0c66"-"\u0c6f", "\u0ce6"-"\u0cef", "\u0d66"-"\u0d6f", "\u0e50"-"\u0e59", "\u0ed0"-"\u0ed9", "\u1040"-"\u1049" ] > } /** * Token for "dynamic subscripts", which are one of: [^], [|], [$], and [*]. The * appropriate constant from the DynamicSubscript class is stored in the token manager's * "value" field. */ TOKEN: { < DYNAMIC_SUBSCRIPT: "[" ["^","|","$","*"] "]" > { switch (image.charAt(1)) { case '^': literalValue = DynamicSubscript.first; break; case '|': literalValue = DynamicSubscript.mid; break; case '$': literalValue = DynamicSubscript.last; break; case '*': literalValue = DynamicSubscript.all; break; } } } /** * Character and string literals, whose object value is stored in the token manager's * "literalValue" field. */ MORE: { "`" : WithinBackCharLiteral | "'" { stringBuffer = new StringBuffer(); }: WithinCharLiteral | "\"" { stringBuffer = new StringBuffer(); }: WithinStringLiteral } MORE: { < ESC: "\\" ( ["n","r","t","b","f","\\","'","`","\""] | (["0"-"3"])? ["0"-"7"] (["0"-"7"])? ) > { charValue = escapeChar(); stringBuffer.append(charValue); } | < (~["'","\\"]) > { charValue = image.charAt( image.length()-1 ); stringBuffer.append(charValue); } } TOKEN: { < CHAR_LITERAL: "'"> { if (stringBuffer.length() == 1) { literalValue = charValue; } else { literalValue = new String( stringBuffer ); } } : DEFAULT } MORE: { < BACK_CHAR_ESC: > { charValue = escapeChar(); } | < (~["`","\\"]) > { charValue = image.charAt( image.length()-1 ); } } TOKEN: { < BACK_CHAR_LITERAL: "`"> { literalValue = charValue; }: DEFAULT } MORE: { < STRING_ESC: > { stringBuffer.append( escapeChar() ); } | < (~["\"","\\"]) > { stringBuffer.append( image.charAt(image.length()-1) ); } } TOKEN: { { literalValue = new String( stringBuffer ); } : DEFAULT } /** * Integer or real Numeric literal, whose object value is stored in the token manager's * "literalValue" field. */ TOKEN: { < INT_LITERAL: ( "0" (["0"-"7"])* | ["1"-"9"] (["0"-"9"])* | "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ ) (["l","L","h","H"])? > { literalValue = makeInt(); } | < FLT_LITERAL: ( ()? ()? | ()? | ) > { literalValue = makeFloat(); } | < #DEC_FLT: (["0"-"9"])+ "." (["0"-"9"])* | "." (["0"-"9"])+ > | < #DEC_DIGITS: (["0"-"9"])+ > | < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > | < #FLT_SUFF: ["d","D","f","F","b","B"] > } ================================================ FILE: ognl/src/test/java/ClassInDefaultPackage.java ================================================ @SuppressWarnings("unused") class ClassInDefaultPackage { public static final int CONST = 99; } ================================================ FILE: ognl/src/test/java/com/sun/test/AnotherInternalClass.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.sun.test; /** * This class is in the "com.sun.test" package to simulate internal classes * for testing accessibility detection. */ public class AnotherInternalClass { public String getValue() { return "com.sun.internal"; } } ================================================ FILE: ognl/src/test/java/ognl/DefaultMemberAccess.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Member; import java.lang.reflect.Modifier; /** * This class provides methods for setting up and restoring * access in a Field. Java 2 provides access utilities for setting * and getting fields that are non-public. This object provides * coarse-grained access controls to allow access to private, protected * and package protected members. This will apply to all classes * and members. */ public class DefaultMemberAccess> implements MemberAccess { private static final AccessibleObjectHandler _accessibleObjectHandler = new AccessibleObjectHandler() {}; public boolean allowPrivateAccess; public boolean allowProtectedAccess; public boolean allowPackageProtectedAccess; public DefaultMemberAccess(boolean allowAllAccess) { this(allowAllAccess, allowAllAccess, allowAllAccess); } public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess) { super(); this.allowPrivateAccess = allowPrivateAccess; this.allowProtectedAccess = allowProtectedAccess; this.allowPackageProtectedAccess = allowPackageProtectedAccess; } public boolean getAllowPrivateAccess() { return allowPrivateAccess; } public void setAllowPrivateAccess(boolean value) { allowPrivateAccess = value; } public boolean getAllowProtectedAccess() { return allowProtectedAccess; } public void setAllowProtectedAccess(boolean value) { allowProtectedAccess = value; } public boolean getAllowPackageProtectedAccess() { return allowPackageProtectedAccess; } public void setAllowPackageProtectedAccess(boolean value) { allowPackageProtectedAccess = value; } public Object setup(C context, Object target, Member member, String propertyName) { Object result = null; if (isAccessible(context, target, member, propertyName)) { AccessibleObject accessible = (AccessibleObject) member; if (!accessible.canAccess(target)) { result = Boolean.FALSE; _accessibleObjectHandler.setAccessible(accessible, true); } } return result; } public void restore(C context, Object target, Member member, String propertyName, Object state) { if (state != null) { final AccessibleObject accessible = (AccessibleObject) member; final boolean stateboolean = ((Boolean) state).booleanValue(); // Using twice (avoid unboxing) if (!stateboolean) { _accessibleObjectHandler.setAccessible(accessible, stateboolean); } else { throw new IllegalArgumentException("Improper restore state [" + stateboolean + "] for target [" + target + "], member [" + member + "], propertyName [" + propertyName + "]"); } } } /** * Returns true if the given member is accessible or can be made accessible * by this object. * * @param context the current execution context (not used). * @param target the Object to test accessibility for (not used). * @param member the Member to test accessibility for. * @param propertyName the property to test accessibility for (not used). * @return true if the member is accessible in the context, false otherwise. */ public boolean isAccessible(C context, Object target, Member member, String propertyName) { int modifiers = member.getModifiers(); boolean result = Modifier.isPublic(modifiers); if (!result) { if (Modifier.isPrivate(modifiers)) { result = getAllowPrivateAccess(); } else { if (Modifier.isProtected(modifiers)) { result = getAllowProtectedAccess(); } else { result = getAllowPackageProtectedAccess(); } } } return result; } } ================================================ FILE: ognl/src/test/java/ognl/ExcludedObjectMemberAccess.java ================================================ /* * Copyright 2020 OGNL Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ognl; import java.lang.reflect.Member; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * This class provides simple functionality for mark / unmark an object as inaccessible */ public class ExcludedObjectMemberAccess extends DefaultMemberAccess { private final List excludedObjects = new ArrayList<>(); // Any field or method in this list will be inaccessible public ExcludedObjectMemberAccess(boolean allowAllAccess) { super(allowAllAccess); } public ExcludedObjectMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess) { super(allowPrivateAccess, allowProtectedAccess, allowPackageProtectedAccess); } public boolean isAccessible(OgnlContext context, Object target, Member member, String propertyName) { if (excludedObjects.contains(member)) { return false; } return super.isAccessible(context, target, member, propertyName); } public void exclude(Object obj) { excludedObjects.add(obj); } public void removeExclusion(Object obj) { excludedObjects.remove(obj); } } ================================================ FILE: ognl/src/test/java/ognl/ObjectPropertyAccessorTest.java ================================================ /* * Copyright 2020 OGNL Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ognl; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; /** * Tests various methods / functionality of {@link ObjectPropertyAccessor}. */ class ObjectPropertyAccessorTest { private OgnlContext context; private ObjectPropertyAccessor propertyAccessor; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null, new ExcludedObjectMemberAccess(false)); propertyAccessor = new ObjectPropertyAccessor(); } /** * Public class for "setPossibleProperty" method tests. */ public static class SimplePublicClass { private String gender = "male"; public String email = "test@test.com"; private String name = "name"; private String age = "18"; @SuppressWarnings("unused") public void setGender(String gender) { this.gender = gender; } @SuppressWarnings("unused") private void setEmail(String email) { this.email = email; } @SuppressWarnings("unused") private void setName(String email) { this.email = email; } @SuppressWarnings("unused") public void setname(String name) { this.name = name; } @SuppressWarnings("unused") private void setAge(String age) { this.age = age; } @SuppressWarnings("unused") public void setage(String age) { this.age = age; } } public static class KafkaFetcher { private final List> completedFutures = Collections.emptyList(); @SuppressWarnings("unused") public boolean hasCompletedFutures() { return !completedFutures.isEmpty(); } } @Test void testGetPossibleProperty() throws OgnlException { KafkaFetcher fetcher = new KafkaFetcher(); assertEquals(Boolean.FALSE, propertyAccessor.getPossibleProperty(context, fetcher, "completedFutures")); OgnlContext defaultContext = Ognl.createDefaultContext(null, new ExcludedObjectMemberAccess(true)); defaultContext.setIgnoreReadMethods(true); assertEquals(Collections.emptyList(), new ObjectPropertyAccessor().getPossibleProperty(defaultContext, fetcher, "completedFutures")); } @Test void testSetPossibleProperty() throws OgnlException { SimplePublicClass simplePublic = new SimplePublicClass(); // 1. when set method is accessible and set method assertNotSame(OgnlRuntime.NotFound, propertyAccessor.setPossibleProperty(context, simplePublic, "gender", "female")); assertEquals("female", simplePublic.gender); // 2. when set method is NOT accessible and fallback to set field (field is accessible) assertNotSame(OgnlRuntime.NotFound, propertyAccessor.setPossibleProperty(context, simplePublic, "email", "admin@admin.com")); assertEquals("admin@admin.com", simplePublic.email); // 3. when set method is NOT accessible, field is NOT accessible, fallback to write method (write method is accessible) Method setMethod = OgnlRuntime.getSetMethod(context, SimplePublicClass.class, "name"); assertNotNull(setMethod); assertEquals("setName", setMethod.getName()); Method writeMethod = OgnlRuntime.getWriteMethod(SimplePublicClass.class, "name", null); assertNotNull(writeMethod); assertEquals("setname", writeMethod.getName()); assertNotSame(OgnlRuntime.NotFound, propertyAccessor.setPossibleProperty(context, simplePublic, "name", "new name")); assertEquals("new name", simplePublic.name); // 4. when set method is NOT accessible, field is NOT accessible, fallback to write method (write method is NOT accessible) Method ageWriteMethod = OgnlRuntime.getWriteMethod(SimplePublicClass.class, "age", null); ((ExcludedObjectMemberAccess) context.getMemberAccess()).exclude(ageWriteMethod); assertNotNull(ageWriteMethod); assertEquals("setage", ageWriteMethod.getName()); assertFalse(context.getMemberAccess().isAccessible(context, simplePublic, ageWriteMethod, "age")); setMethod = OgnlRuntime.getSetMethod(context, SimplePublicClass.class, "age"); assertNotNull(setMethod); assertEquals("setAge", setMethod.getName()); assertEquals(OgnlRuntime.NotFound, propertyAccessor.setPossibleProperty(context, simplePublic, "age", "99")); assertEquals("18", simplePublic.age); } } ================================================ FILE: ognl/src/test/java/ognl/OgnlContextTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.test.objects.Root; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class OgnlContextTest { private static OgnlContext createOgnlContext() { return new OgnlContext(new DefaultMemberAccess(false), null, null); } @Test void traceEvaluation_shouldBeEnabled() { OgnlContext context = createOgnlContext(); context.setTraceEvaluations(true); assertTrue(context.isTraceEvaluations()); assertEquals(Boolean.TRUE, context.get("_traceEvaluations")); } @Test void keepLastEvaluation_shouldBeEnabled() { OgnlContext context = createOgnlContext(); context.setKeepLastEvaluation(true); assertTrue(context.isKeepLastEvaluation()); assertEquals(Boolean.TRUE, context.get("_keepLastEvaluation")); } @Test void allValues_shouldBeStored() { OgnlContext context = createOgnlContext(); Map values = new HashMap<>(); values.put("key1", "value1"); values.put("key2", "value2"); context.setValues(values); assertEquals(values, context.getValues()); } @Test void classResolver_shouldNotBeNull() { OgnlContext context = createOgnlContext(); assertNotNull(context.getClassResolver()); assertEquals(DefaultClassResolver.class, context.getClassResolver().getClass()); } @Test void typeConverted_shouldNotBeNull() { OgnlContext context = createOgnlContext(); assertNotNull(context.getTypeConverter()); assertEquals(DefaultTypeConverter.class, context.getTypeConverter().getClass()); } @Test void memberAccess_shouldNotBeNull() { OgnlContext context = createOgnlContext(); assertNotNull(context.getMemberAccess()); assertEquals(DefaultMemberAccess.class, context.getMemberAccess().getClass()); } @Test void root_shouldInitAccessorAndType() { OgnlContext context = createOgnlContext(); Root root = new Root(); context.setRoot(root); assertNotNull(context.getRoot()); assertNotNull(context.getCurrentObject()); assertNull(context.getCurrentNode()); assertNull(context.getCurrentAccessor()); assertNull(context.getFirstAccessor()); assertNull(context.getPreviousAccessor()); assertNotNull(context.getCurrentType()); assertEquals(Root.class, context.getCurrentType()); assertNotNull(context.getFirstType()); assertEquals(Root.class, context.getFirstType()); assertNull(context.getPreviousType()); assertEquals(root, context.get("root")); } @Test void currentEvaluation_shouldNotBeNull() throws OgnlException { OgnlContext context = createOgnlContext(); Root root = new Root(); context.setRoot(root); Object result = Ognl.getValue("index", context, root); assertNotNull(result); assertEquals(1, result); assertNull(context.getCurrentEvaluation()); assertNull(context.getRootEvaluation()); assertNull(context.getLastEvaluation()); } @Test void ignoreReadMethod() { OgnlContext context = createOgnlContext(); assertFalse(context.isIgnoreReadMethods()); assertEquals(Boolean.FALSE, context.get("_ignoreReadMethods")); context.setIgnoreReadMethods(true); assertTrue(context.isIgnoreReadMethods()); assertEquals(Boolean.TRUE, context.get("_ignoreReadMethods")); assertEquals(Boolean.TRUE, context.put("_ignoreReadMethods", false)); assertFalse(context.isIgnoreReadMethods()); assertEquals(Boolean.FALSE, context.get("_ignoreReadMethods")); assertThrows(IllegalArgumentException.class, () -> context.remove("_ignoreReadMethods")); } @Test void reservedKeywords() { // given OgnlContext context = createOgnlContext(); Object root = new Object(); // when context.put("root", root); // then assertSame(root, context.get("root")); assertNull(context.getValues().get("root")); // when context.put("this", root); // then assertSame(root, context.get("this")); assertNull(context.getValues().get("this")); // when assertFalse(context.isTraceEvaluations()); context.put("_traceEvaluations", Boolean.TRUE); // then assertSame(Boolean.TRUE, context.get("_traceEvaluations")); assertTrue(context.isTraceEvaluations()); assertNull(context.getValues().get("_traceEvaluations")); // given Evaluation evaluation = new Evaluation(new ASTConst(0), root); // when assertNull(context.getLastEvaluation()); context.put("_lastEvaluation", evaluation); // then assertSame(evaluation, context.get("_lastEvaluation")); assertSame(evaluation, context.getLastEvaluation()); assertNull(context.getValues().get("_lastEvaluation")); // when assertFalse(context.isKeepLastEvaluation()); context.put("_keepLastEvaluation", Boolean.TRUE); // then assertSame(Boolean.TRUE, context.get("_keepLastEvaluation")); assertTrue(context.isKeepLastEvaluation()); assertNull(context.getValues().get("_keepLastEvaluation")); } @Test void memberAccessIsRequired() { try { new OgnlContext((MemberAccess) null, null, null); } catch (Exception e) { assertInstanceOf(IllegalArgumentException.class, e); assertEquals("MemberAccess implementation must be provided - null not permitted!", e.getMessage()); } } @Test void defaultClassResolverAndTypeConverter() { // given & when OgnlContext context = new OgnlContext(new DefaultMemberAccess(false), null, null); // then assertTrue(context.getValues().isEmpty()); assertTrue(context.isEmpty()); assertInstanceOf(DefaultClassResolver.class, context.getClassResolver()); assertInstanceOf(DefaultTypeConverter.class, context.getTypeConverter()); } } ================================================ FILE: ognl/src/test/java/ognl/OgnlRuntimeAccessibilityTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Unit tests for OgnlRuntime.isLikelyAccessible() method. *

* These tests verify the logic for determining if a class is accessible, * considering the Java module system and known internal packages. */ class OgnlRuntimeAccessibilityTest { @Test void interfaceIsAlwaysAccessible() { // Interfaces should always be considered accessible assertTrue(OgnlRuntime.isLikelyAccessible(List.class)); assertTrue(OgnlRuntime.isLikelyAccessible(Map.class)); assertTrue(OgnlRuntime.isLikelyAccessible(Runnable.class)); assertTrue(OgnlRuntime.isLikelyAccessible(Comparable.class)); } @Test void standardJdkClassesAreAccessible() { // Standard JDK classes in exported packages should be accessible assertTrue(OgnlRuntime.isLikelyAccessible(String.class)); assertTrue(OgnlRuntime.isLikelyAccessible(Integer.class)); assertTrue(OgnlRuntime.isLikelyAccessible(Object.class)); assertTrue(OgnlRuntime.isLikelyAccessible(StringBuilder.class)); } @Test void javaUtilClassesAreAccessible() { // java.util classes should be accessible assertTrue(OgnlRuntime.isLikelyAccessible(java.util.ArrayList.class)); assertTrue(OgnlRuntime.isLikelyAccessible(java.util.HashMap.class)); assertTrue(OgnlRuntime.isLikelyAccessible(java.util.Date.class)); } @Test void sunPackageClassesAreInaccessible() throws Exception { // Try to load actual sun.* classes if available // These are internal JDK classes that should be detected as inaccessible // Try to find a sun.* class (may not be available in all JDK versions) try { Class sunClass = Class.forName("sun.misc.Unsafe"); assertFalse(OgnlRuntime.isLikelyAccessible(sunClass), "sun.misc.Unsafe should be detected as inaccessible"); } catch (ClassNotFoundException e) { // sun.misc.Unsafe not available, skip this check } // Try sun.security classes try { Class sunSecurityClass = Class.forName("sun.security.action.GetPropertyAction"); assertFalse(OgnlRuntime.isLikelyAccessible(sunSecurityClass), "sun.security classes should be detected as inaccessible"); } catch (ClassNotFoundException e) { // Class not available, skip this check } } @Test void comSunPackageClassesAreInaccessible() throws Exception { // Try to load actual com.sun.* classes if available try { Class comSunClass = Class.forName("com.sun.jndi.ldap.LdapCtx"); assertFalse(OgnlRuntime.isLikelyAccessible(comSunClass), "com.sun.* classes should be detected as inaccessible"); } catch (ClassNotFoundException e) { // Class not available, skip this check } } @Test void customClassesAreAccessible() { // User-defined classes should be accessible assertTrue(OgnlRuntime.isLikelyAccessible(OgnlRuntimeAccessibilityTest.class)); assertTrue(OgnlRuntime.isLikelyAccessible(TestHelperClass.class)); } @Test void innerClassesAreAccessible() { // Inner classes should be accessible assertTrue(OgnlRuntime.isLikelyAccessible(TestHelperClass.InnerClass.class)); } @Test void simulatedSunPackageClassIsInaccessible() { // Test with our simulated sun.test.* class assertFalse(OgnlRuntime.isLikelyAccessible(sun.test.SimulatedInternalClass.class), "Classes in sun.test package should be detected as inaccessible"); } @Test void simulatedComSunPackageClassIsInaccessible() { // Test with our simulated com.sun.test.* class assertFalse(OgnlRuntime.isLikelyAccessible(com.sun.test.AnotherInternalClass.class), "Classes in com.sun.test package should be detected as inaccessible"); } @Test void interfaceInSunPackageIsAccessible() { // Even though it's in sun.* package, interfaces are always accessible assertTrue(OgnlRuntime.isLikelyAccessible(sun.test.PublicTestInterface.class), "Interfaces should always be accessible, even in sun.* packages"); } // Helper classes for testing public static class TestHelperClass { public static class InnerClass { } } } ================================================ FILE: ognl/src/test/java/ognl/OgnlRuntimeMethodsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import ognl.test.objects.BaseGeneric; import ognl.test.objects.Bean2; import ognl.test.objects.FormImpl; import ognl.test.objects.GameGeneric; import ognl.test.objects.GameGenericObject; import ognl.test.objects.GenericCracker; import ognl.test.objects.GenericService; import ognl.test.objects.GenericServiceImpl; import ognl.test.objects.GetterMethods; import ognl.test.objects.IComponent; import ognl.test.objects.IForm; import ognl.test.objects.ListSource; import ognl.test.objects.ListSourceImpl; import ognl.test.objects.OtherEnum; import ognl.test.objects.Root; import ognl.test.objects.SetterReturns; import ognl.test.objects.SubclassSyntheticObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.beans.PropertyDescriptor; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.fail; /** * Tests various methods / functionality of {@link OgnlRuntime}. */ class OgnlRuntimeMethodsTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } @Test void test_Get_Super_Or_Interface_Class() { ListSource list = new ListSourceImpl(); Method m = OgnlRuntime.getReadMethod(list.getClass(), "total"); assertNotNull(m); assertEquals(ListSource.class, OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, list.getClass())); } @Test void test_Get_Private_Class() { List list = Arrays.asList("hello", "world"); Method m = OgnlRuntime.getReadMethod(list.getClass(), "iterator"); assertNotNull(m); assertEquals(Iterable.class, OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, list.getClass())); } @Test void test_Complicated_Inheritance() { IForm form = new FormImpl(); Method m = OgnlRuntime.getWriteMethod(form.getClass(), "clientId"); assertNotNull(m); assertEquals(IComponent.class, OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, form.getClass())); } @Test void test_Get_Read_Method() { Method m = OgnlRuntime.getReadMethod(Bean2.class, "pageBreakAfter"); assertNotNull(m); assertEquals("isPageBreakAfter", m.getName()); } @Test void test_Get_Read_Field() { Method m = OgnlRuntime.getReadMethod(Bean2.class, "code"); assertNull(m); Field field = OgnlRuntime.getField(Bean2.class, "code"); assertNotNull(field); assertEquals("code", field.getName()); } @SuppressWarnings("unused") static class TestGetters { public boolean isEditorDisabled() { return false; } public boolean isDisabled() { return true; } public boolean isNotAvailable() { return false; } public boolean isAvailable() { return true; } } @Test void test_Get_Read_Method_Multiple() { Method m = OgnlRuntime.getReadMethod(TestGetters.class, "disabled"); assertNotNull(m); assertEquals("isDisabled", m.getName()); } @Test void test_Get_Read_Method_Multiple_Boolean_Getters() { Method m = OgnlRuntime.getReadMethod(TestGetters.class, "available"); assertNotNull(m); assertEquals("isAvailable", m.getName()); m = OgnlRuntime.getReadMethod(TestGetters.class, "notAvailable"); assertNotNull(m); assertEquals("isNotAvailable", m.getName()); } @Test void test_Find_Method_Mixed_Boolean_Getters() { Method m = OgnlRuntime.getReadMethod(GetterMethods.class, "allowDisplay"); assertNotNull(m); assertEquals("getAllowDisplay", m.getName()); } @Test void test_Get_Appropriate_Method() throws Exception { ListSource list = new ListSourceImpl(); Object ret = OgnlRuntime.callMethod(context, list, "addValue", new String[]{null}); assertNotNull(ret); } @Test void test_Call_Static_Method_Invalid_Class() { Exception exception = assertThrows(MethodFailedException.class, () -> OgnlRuntime.callStaticMethod(context, "made.up.Name", "foo", null)); assertTrue(exception.getMessage().contains("made.up.Name")); } @Test void test_Setter_Returns() throws Exception { SetterReturns root = new SetterReturns(); Method m = OgnlRuntime.getWriteMethod(root.getClass(), "value"); assertNotNull(m); Ognl.setValue("value", context, root, "12__"); assertEquals("12__", Ognl.getValue("value", context, root)); } @Test void test_Call_Method_VarArgs() throws Exception { GenericService service = new GenericServiceImpl(); GameGenericObject argument = new GameGenericObject(); Object[] args = new Object[2]; args[0] = argument; assertEquals("Halo 3", OgnlRuntime.callMethod(context, service, "getFullMessageFor", args)); } @Test void test_Class_Cache_Inspector() throws Exception { OgnlRuntime.clearCache(); OgnlRuntime.clearAdditionalCache(); // Testing no exception only. assertEquals(0, OgnlRuntime.cache.propertyDescriptorCache.getSize()); assertEquals(0, OgnlRuntime.cache.genericMethodParameterTypesCache.getSize()); Root root = new Root(); Node expr = Ognl.compileExpression(context, root, "property.bean3.value != null"); assertTrue((Boolean) expr.getAccessor().get(context, root)); int size = OgnlRuntime.cache.propertyDescriptorCache.getSize(); assertTrue(size > 0); OgnlRuntime.clearCache(); OgnlRuntime.clearAdditionalCache(); // Testing no exception only. assertEquals(0, OgnlRuntime.cache.propertyDescriptorCache.getSize()); assertEquals(0, OgnlRuntime.cache.genericMethodParameterTypesCache.getSize()); // now register class cache prevention OgnlRuntime.setClassCacheInspector(new TestCacheInspector()); expr = Ognl.compileExpression(context, root, "property.bean3.value != null"); assertTrue((Boolean) expr.getAccessor().get(context, root)); assertEquals((size - 1), OgnlRuntime.cache.propertyDescriptorCache.getSize()); } static class TestCacheInspector implements ClassCacheInspector { public boolean shouldCache(Class type) { return type != null && type != Root.class; } } @Test void test_Set_Generic_Parameter_Types() { Method m = OgnlRuntime.getSetMethod(context, GenericCracker.class, "param"); assertNotNull(m); Class[] types = m.getParameterTypes(); assertEquals(1, types.length); assertEquals(Integer.class, types[0]); } @Test void test_Get_Generic_Parameter_Types() { Method m = OgnlRuntime.getGetMethod(GenericCracker.class, "param"); assertNotNull(m); assertEquals(Integer.class, m.getReturnType()); } @Test void test_Find_Parameter_Types() { Method m = OgnlRuntime.getSetMethod(context, GameGeneric.class, "ids"); assertNotNull(m); Class[] types = OgnlRuntime.findParameterTypes(GameGeneric.class, m); assertEquals(1, types.length); assertEquals(Long[].class, types[0]); } @Test void test_Find_Parameter_Types_Superclass() { Method m = OgnlRuntime.getSetMethod(context, BaseGeneric.class, "ids"); assertNotNull(m); Class[] types = OgnlRuntime.findParameterTypes(BaseGeneric.class, m); assertEquals(1, types.length); assertEquals(Serializable[].class, types[0]); } @Test void test_Get_Declared_Methods_With_Synthetic_Methods() { List result = OgnlRuntime.getDeclaredMethods(SubclassSyntheticObject.class, "list", false); // synthetic method would be "public volatile java.util.List ognl.test.objects.SubclassSyntheticObject.getList()", // causing method return size to be 3 assertEquals(2, result.size()); } @Test void test_Get_Property_Descriptors_With_Synthetic_Methods() throws Exception { PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(SubclassSyntheticObject.class, "list"); assertNotNull(pd); assertTrue(OgnlRuntime.isMethodCallable(pd.getReadMethod())); } public static class GenericParent { @SuppressWarnings("unused") void save(T entity) { } } public static class StringChild extends GenericParent { } public static class LongChild extends GenericParent { } /** * Tests OGNL parameter discovery. */ @Test void testOGNLParameterDiscovery() throws NoSuchMethodException { Method saveMethod = GenericParent.class.getDeclaredMethod("save", Object.class); Class[] longClass = OgnlRuntime.findParameterTypes(LongChild.class, saveMethod); assertNotSame(String.class, longClass[0]); assertSame(Long.class, longClass[0]); Class[] stringClass = OgnlRuntime.findParameterTypes(StringChild.class, saveMethod); assertNotSame(Long.class, stringClass[0], "The cached parameter types from previous calls are used"); assertSame(String.class, stringClass[0]); } @Test void testBangOperator() throws Exception { Object value = Ognl.getValue("!'false'", context, new Object()); assertEquals(Boolean.TRUE, value); } @Test void testGetStaticField() throws Exception { Object obj = OgnlRuntime.getStaticField(context, "ognl.test.objects.Root", "SIZE_STRING"); assertEquals(Root.SIZE_STRING, obj); } @Test void testGetStaticFieldEnum() throws Exception { Object obj = OgnlRuntime.getStaticField(context, "ognl.test.objects.OtherEnum", "ONE"); assertEquals(OtherEnum.ONE, obj); } @Test void testGetStaticFieldEnumStatic() throws Exception { Object obj = OgnlRuntime.getStaticField(context, "ognl.test.objects.OtherEnum", "STATIC_STRING"); assertEquals(OtherEnum.STATIC_STRING, obj); } /** * This test indirectly confirms an error output (syserr) is no longer produced when OgnlRuntime * encounters the condition reported in issue #17. {@link OgnlRuntime#findBestMethod(List, Class, String, Class[])} * can find two appropriate methods with the same score where one is abstract and one is concrete. Either * choice in that scenario actually worked when invoked, but produced the unwanted syserr output. */ @Test void testAbstractConcreteMethodScoringNoSysErr() throws Exception { OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); ObjectMethodAccessor methodAccessor = new ObjectMethodAccessor(); ConcreteTestClass concreteTestClass = new ConcreteTestClass(); Object result = methodAccessor.callMethod(context, concreteTestClass, "testMethod", new Object[]{"Test", 1}); // The "Two methods with same score(0) ..." error output should no longer be seen with the above call. assertEquals("Test" + 1, result, "Result not concatenation of parameters ?"); } /** * Abstract test class for issue #42 - equal score syserr output for abstract class/method hierarchy. * * @param */ abstract static class AbstractTestClass { @SuppressWarnings("unused") public abstract String testMethod(T element, int i); } /** * Concrete test class for issue #42 - equal score syserr output for abstract class/method hierarchy. */ static class ConcreteTestClass extends AbstractTestClass { public String testMethod(String element, int i) { return element + i; } } /** * Protected class for synthetic/bridge method tests. */ protected static class ProtectedParent { @SuppressWarnings("unused") public void setName(String name) { } public String getName() { return "name"; } } /** * Public descendant class for synthetic/bridge method tests. */ public static class PublicChild extends ProtectedParent { } /** * Test that synthetic bridge read methods can be found successfully. *

* Note: Only bridge methods should qualify, non-bridge synthetic methods should not. */ @Test void testSyntheticBridgeReadMethod() { assertNotNull(OgnlRuntime.getReadMethod(PublicChild.class, "name")); } /** * Test that synthetic bridge write methods can be found successfully. *

* Note: Only bridge methods should qualify, non-bridge synthetic methods should not. */ @Test void testSyntheticBridgeWriteMethod() { assertNotNull(OgnlRuntime.getWriteMethod(PublicChild.class, "name", new Class[]{String.class})); } /** * Public class for "is callable" method tests. */ public static class SimplePublicClass { String name = "name contents"; public String getName() { return name; } } /** * Public class with non-public nested class for "is callable" method tests. */ public static class SimpleNestingClass { static class NestedClass { // do not use "final" private String name = "nested name contents"; } public String getNestedName() { return new NestedClass().name; // Should force creation of a synthetic method for NestedClass (to access its name field). } } /** * Test that normal non-synthetic methods are considered callable by both isMethodCallable() and isMethodCallable_BridgeOrNonSynthetic(). */ @Test void testConfirmStandardMethodCallability() { Method method = null; try { method = SimplePublicClass.class.getDeclaredMethod("getName", (Class[]) null); } catch (NoSuchMethodException nsme) { fail("SimplePublicClass.getName() method retrieval by reflection failed (NoSuchMethodException) ?"); } assertNotNull(method, "getName() method retrieval failed ?"); assertFalse(method.isBridge() || method.isSynthetic(), "SimplePublicClass.getName() is a synthetic or bridge method ?"); assertTrue(OgnlRuntime.isMethodCallable(method), "SimplePublicClass.getName() is not considered callable by isMethodCallable() ?"); assertTrue(OgnlRuntime.isMethodCallable_BridgeOrNonSynthetic(method), "SimplePublicClass.getName() is not considered callable by isMethodCallable_BridgeOrNonSynthetic() ?"); } /** * Test that bridge methods ARE considered callable by isMethodCallable_BridgeOrNonSynthetic() ONLY, and NOT by isMethodCallable(). */ @Test void testConfirmBridgeMethodCallability() { Method method = null; try { method = PublicChild.class.getDeclaredMethod("getName", (Class[]) null); } catch (NoSuchMethodException nsme) { fail("PublicChild.getName() method retrieval by reflection failed (NoSuchMethodException) ?"); } assertNotNull(method, "getName() method retrieval failed ?"); assertTrue(method.isBridge(), "PublicChild.getName() is not a bridge method ?"); assertFalse(OgnlRuntime.isMethodCallable(method), "PublicChild.getName() is considered callable by isMethodCallable() ?"); assertTrue(OgnlRuntime.isMethodCallable_BridgeOrNonSynthetic(method), "PublicChild.getName() is not considered callable by isMethodCallable_BridgeOrNonSynthetic() ?"); try { Class[] argumentTypes = {String.class}; method = PublicChild.class.getDeclaredMethod("setName", argumentTypes); } catch (NoSuchMethodException nsme) { fail("PublicChild.setName() method retrieval by reflection failed (NoSuchMethodException) ?"); } assertNotNull(method, "setName() method retrieval failed ?"); assertTrue(method.isBridge(), "PublicChild.setName() is not a bridge method ?"); assertFalse(OgnlRuntime.isMethodCallable(method), "PublicChild.setName() is considered callable by isMethodCallable() ?"); assertTrue(OgnlRuntime.isMethodCallable_BridgeOrNonSynthetic(method), "PublicChild.setName() is not considered callable by isMethodCallable_BridgeOrNonSynthetic() ?"); } /** * Test that no synthetic method is created. */ @Test void testConfirmNoSyntheticMethod() throws Exception { Method[] methods = SimpleNestingClass.NestedClass.class.getDeclaredMethods(); assertNotNull(methods, "Nested class has no methods ?"); Field field = SimpleNestingClass.NestedClass.class.getDeclaredField("name"); field.setAccessible(true); assertEquals("nested name contents", field.get(new SimpleNestingClass.NestedClass())); assertEquals("nested name contents", new SimpleNestingClass().getNestedName()); } /** * Public class for "setFieldValue" method tests. */ public static class SimpleFieldClass { public static String NAME = "name"; public final List numbers = Arrays.asList("one", "two", "three"); public String gender = "male"; public String email = "test@test.com"; private String address = "1 Glen st"; } @Test void testSetFieldValueWhenCheckAccess() throws OgnlException, NoSuchFieldException { SimpleFieldClass simpleField = new SimpleFieldClass(); // verify that the static & final field is NOT accessible and bypass set field value assertFalse(OgnlRuntime.setFieldValue(context, simpleField, "NAME", "new name", true)); assertEquals("name", SimpleFieldClass.NAME); assertFalse(OgnlRuntime.setFieldValue(context, simpleField, "numbers", Collections.singletonList("four"), true)); assertEquals(3, simpleField.numbers.size()); // verify that the field is accessible and set field value successfully Field genderField = SimpleFieldClass.class.getDeclaredField("gender"); assertTrue(context.getMemberAccess().isAccessible(context, simpleField, genderField, null)); assertTrue(OgnlRuntime.setFieldValue(context, simpleField, "gender", "female", true)); assertEquals("female", simpleField.gender); // verify that the field is NOT accessible, and bypass set field value Field addressField = SimpleFieldClass.class.getDeclaredField("address"); assertFalse(context.getMemberAccess().isAccessible(context, simpleField, addressField, null)); assertFalse(OgnlRuntime.setFieldValue(context, simpleField, "address", "2 Glen st", true)); assertEquals("1 Glen st", simpleField.address); } @Test void testSetFieldValueWhenNotCheckAccess() throws OgnlException, NoSuchFieldException { ExcludedObjectMemberAccess memberAccess = new ExcludedObjectMemberAccess(false); OgnlContext context = Ognl.createDefaultContext(null, memberAccess); SimpleFieldClass simpleField = new SimpleFieldClass(); // verify that the static & final field is NOT accessible and bypass set field value assertFalse(OgnlRuntime.setFieldValue(context, simpleField, "NAME", "new name", true)); assertEquals("name", SimpleFieldClass.NAME); assertFalse(OgnlRuntime.setFieldValue(context, simpleField, "numbers", Collections.singletonList("four"), true)); assertEquals(3, simpleField.numbers.size()); // verify that the field is accessible and set field value successfully Field genderField = SimpleFieldClass.class.getDeclaredField("gender"); assertTrue(context.getMemberAccess().isAccessible(context, simpleField, genderField, null)); assertTrue(OgnlRuntime.setFieldValue(context, simpleField, "gender", "female", true)); assertEquals("female", simpleField.gender); // verify that even the field is NOT accessible, and it processes to set field value successfully Field emailField = SimpleFieldClass.class.getDeclaredField("email"); memberAccess.exclude(emailField); assertFalse(memberAccess.isAccessible(context, simpleField, emailField, null)); OgnlRuntime.setFieldValue(context, simpleField, "email", "admin@admin.com", true); assertEquals("test@test.com", simpleField.email); OgnlRuntime.setFieldValue(context, simpleField, "email", "admin@admin.com", false); assertEquals("admin@admin.com", simpleField.email); // verify that even the field is NOT accessible, and it processes to set field value but throws NoSuchPropertyException (as for private field) Field addressField = SimpleFieldClass.class.getDeclaredField("address"); memberAccess.exclude(addressField); assertFalse(memberAccess.isAccessible(context, simpleField, addressField, null)); try { OgnlRuntime.setFieldValue(context, simpleField, "address", "2 Glen st", true); } catch (NoSuchPropertyException e) { assertEquals("ognl.TestOgnlRuntime$SimpleFieldClass.address", e.getMessage()); assertEquals("1 Glen st", simpleField.address); } } } ================================================ FILE: ognl/src/test/java/ognl/OgnlRuntimeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; public class OgnlRuntimeTest { /** * Test OgnlRuntime version parsing mechanism. */ @Test public void testMajorJavaVersionParse() { // Pre-JDK 9 version strings. assertEquals(5, OgnlRuntime.parseMajorJavaVersion("1.5"), "JDK 5 version check failed ?"); assertEquals(5, OgnlRuntime.parseMajorJavaVersion("1.5.0"), "JDK 5 version check failed ?"); assertEquals(5, OgnlRuntime.parseMajorJavaVersion("1.5.0_21-b11"), "JDK 5 version check failed ?"); assertEquals(6, OgnlRuntime.parseMajorJavaVersion("1.6"), "JDK 6 version check failed ?"); assertEquals(6, OgnlRuntime.parseMajorJavaVersion("1.6.0"), "JDK 6 version check failed ?"); assertEquals(6, OgnlRuntime.parseMajorJavaVersion("1.6.0_43-b19"), "JDK 6 version check failed ?"); assertEquals(7, OgnlRuntime.parseMajorJavaVersion("1.7"), "JDK 7 version check failed ?"); assertEquals(7, OgnlRuntime.parseMajorJavaVersion("1.7.0"), "JDK 7 version check failed ?"); assertEquals(7, OgnlRuntime.parseMajorJavaVersion("1.7.0_79-b15"), "JDK 7 version check failed ?"); assertEquals(8, OgnlRuntime.parseMajorJavaVersion("1.8"), "JDK 8 version check failed ?"); assertEquals(8, OgnlRuntime.parseMajorJavaVersion("1.8.0"), "JDK 8 version check failed ?"); assertEquals(8, OgnlRuntime.parseMajorJavaVersion("1.8.0_201-b20"), "JDK 8 version check failed ?"); assertEquals(8, OgnlRuntime.parseMajorJavaVersion("1.8.0-someopenjdkstyle"), "JDK 8 version check failed ?"); assertEquals(8, OgnlRuntime.parseMajorJavaVersion("1.8.0_201-someopenjdkstyle"), "JDK 8 version check failed ?"); // JDK 9 and later version strings. assertEquals(9, OgnlRuntime.parseMajorJavaVersion("9"), "JDK 9 version check failed ?"); assertEquals(9, OgnlRuntime.parseMajorJavaVersion("9-ea+19"), "JDK 9 version check failed ?"); assertEquals(9, OgnlRuntime.parseMajorJavaVersion("9+100"), "JDK 9 version check failed ?"); assertEquals(9, OgnlRuntime.parseMajorJavaVersion("9-ea+19"), "JDK 9 version check failed ?"); assertEquals(9, OgnlRuntime.parseMajorJavaVersion("9.1.3+15"), "JDK 9 version check failed ?"); assertEquals(9, OgnlRuntime.parseMajorJavaVersion("9-someopenjdkstyle"), "JDK 9 version check failed ?"); assertEquals(10, OgnlRuntime.parseMajorJavaVersion("10"), "JDK 10 version check failed ?"); assertEquals(10, OgnlRuntime.parseMajorJavaVersion("10-ea+11"), "JDK 10 version check failed ?"); assertEquals(10, OgnlRuntime.parseMajorJavaVersion("10+10"), "JDK 10 version check failed ?"); assertEquals(10, OgnlRuntime.parseMajorJavaVersion("10-ea+11"), "JDK 10 version check failed ?"); assertEquals(10, OgnlRuntime.parseMajorJavaVersion("10.1.3+15"), "JDK 10 version check failed ?"); assertEquals(10, OgnlRuntime.parseMajorJavaVersion("10-someopenjdkstyle"), "JDK 10 version check failed ?"); assertEquals(11, OgnlRuntime.parseMajorJavaVersion("11"), "JDK 11 version check failed ?"); assertEquals(11, OgnlRuntime.parseMajorJavaVersion("11-ea+22"), "JDK 11 version check failed ?"); assertEquals(11, OgnlRuntime.parseMajorJavaVersion("11+33"), "JDK 11 version check failed ?"); assertEquals(11, OgnlRuntime.parseMajorJavaVersion("11-ea+19"), "JDK 11 version check failed ?"); assertEquals(11, OgnlRuntime.parseMajorJavaVersion("11.1.3+15"), "JDK 11 version check failed ?"); assertEquals(11, OgnlRuntime.parseMajorJavaVersion("11-someopenjdkstyle"), "JDK 11 version check failed ?"); } /** * Test OgnlRuntime Major Version Check mechanism. */ @Test public void testMajorJavaVersionCheck() { // Ensure no exceptions, basic ouput for test report and sanity check on minimum version. final int majorJavaVersion = OgnlRuntime.detectMajorJavaVersion(); System.out.println("Major Java Version detected: " + majorJavaVersion); assertTrue(majorJavaVersion >= 5, "Major Java Version Check returned value (" + majorJavaVersion + ") less than minimum (5) ?"); } /** * Test OgnlRuntime value for _useStricterInvocation based on the System properties * represented by {@link OgnlRuntime#USE_STRICTER_INVOCATION}. */ @Test public void testUseStricterInvocationStateFlag() { // Ensure no exceptions, basic ouput for test report and sanity check on flag state. final boolean defaultValue = true; // Expected non-configured default boolean optionDefinedInEnvironment = false; // Track if option defined in environment boolean flagValueFromEnvironment = true; // Expected non-configured default try { final String propertyString = System.getProperty(OgnlRuntime.USE_STRICTER_INVOCATION); if (propertyString != null && !propertyString.isEmpty()) { optionDefinedInEnvironment = true; flagValueFromEnvironment = Boolean.parseBoolean(propertyString); } } catch (Exception ex) { // Unavailable (SecurityException, etc.) } if (optionDefinedInEnvironment) { System.out.println("System property " + OgnlRuntime.USE_STRICTER_INVOCATION + " value: " + flagValueFromEnvironment); } else { System.out.println("System property " + OgnlRuntime.USE_STRICTER_INVOCATION + " not present. Default value should be: " + defaultValue); } System.out.println("Current OGNL value for use stricter invocation: " + OgnlRuntime.getUseStricterInvocationValue()); assertEquals(optionDefinedInEnvironment ? flagValueFromEnvironment : defaultValue, OgnlRuntime.getUseStricterInvocationValue(), "Mismatch between system property (or default) and OgnlRuntime _useStricterInvocation flag state ?"); } /** * Test OgnlRuntime stricter invocation mode. */ @Test public void testStricterInvocationMode() { // Ensure no exceptions, basic ouput for test report and sanity check on flag state. // Note: If stricter invocation mode is disabled (due to a system property being set for // the JVM running the test) this test will not fail, but just skip the test. if (OgnlRuntime.getUseStricterInvocationValue()) { try { final Class[] singleClassArgument = new Class[1]; singleClassArgument[0] = int.class; final Method exitMethod = System.class.getMethod("exit", singleClassArgument); try { OgnlRuntime.invokeMethod(System.class, exitMethod, new Object[]{-1}); fail("Somehow got past invocation of a restricted exit call (nonsensical result) ?"); } catch (IllegalAccessException iae) { // Expected failure (failed during invocation) System.out.println("Stricter invocation mode blocked restricted call (as expected). Exception: " + iae); } catch (SecurityException se) { // Possible exception if test is run with an active security manager) System.out.println("Stricter invocation mode blocked by security manager (may be valid). Exception: " + se); } singleClassArgument[0] = String.class; final Method execMethod = Runtime.class.getMethod("exec", singleClassArgument); try { OgnlRuntime.invokeMethod(Runtime.getRuntime(), execMethod, new Object[]{"fakeCommand"}); fail("Somehow got past invocation of a restricted exec call ?"); } catch (IllegalAccessException iae) { // Expected failure (failed during invocation) System.out.println("Stricter invocation mode blocked restricted call (as expected). Exception: " + iae); } catch (SecurityException se) { // Possible exception if test is run with an active security manager) System.out.println("Stricter invocation mode blocked by security manager (may be valid). Exception: " + se); } } catch (Exception ex) { fail("Unable to fully test stricter invocation mode. Exception: " + ex); } } else { System.out.println("Not testing stricter invocation mode (disabled via system property)."); } } /** * Test that stricter invocation mode blocks calls to sun.misc.Unsafe methods. */ @Test void testStricterInvocationBlocksUnsafe() { if (OgnlRuntime.getUseStricterInvocationValue()) { try { final Class unsafeClass = Class.forName("sun.misc.Unsafe"); final Method allocateMethod = unsafeClass.getMethod("allocateMemory", long.class); // Get the Unsafe instance via reflection final java.lang.reflect.Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); final Object unsafeInstance = theUnsafe.get(null); try { OgnlRuntime.invokeMethod(unsafeInstance, allocateMethod, new Object[]{1L}); fail("Should have blocked Unsafe method call"); } catch (IllegalAccessException iae) { // Expected: stricter invocation mode blocks Unsafe } } catch (ClassNotFoundException cnfe) { // sun.misc.Unsafe not available on this JDK, skip } catch (Exception ex) { fail("Unexpected exception testing Unsafe blocking: " + ex); } } } /** * Test that AccessibleObjectHandler default method works correctly. */ @Test void testAccessibleObjectHandlerDefault() throws Exception { AccessibleObjectHandler handler = new AccessibleObjectHandler() {}; Method method = String.class.getMethod("length"); handler.setAccessible(method, true); assertTrue(method.canAccess("test"), "Method should be accessible after setAccessible(true)"); } /** * Test OgnlRuntime value for _useFirstMatchGetSetLookup based on the System property * represented by {@link OgnlRuntime#USE_FIRSTMATCH_GETSET_LOOKUP}. */ @Test public void testUseFirstMatchGetSetStateFlag() { // Ensure no exceptions, basic ouput for test report and sanity check on flag state. final boolean defaultValue = false; // Expected non-configured default boolean optionDefinedInEnvironment = false; // Track if option defined in environment boolean flagValueFromEnvironment = false; // Value result from environment retrieval try { final String propertyString = System.getProperty(OgnlRuntime.USE_FIRSTMATCH_GETSET_LOOKUP); if (propertyString != null && !propertyString.isEmpty()) { optionDefinedInEnvironment = true; flagValueFromEnvironment = Boolean.parseBoolean(propertyString); } } catch (Exception ex) { // Unavailable (SecurityException, etc.) } if (optionDefinedInEnvironment) { System.out.println("System property " + OgnlRuntime.USE_FIRSTMATCH_GETSET_LOOKUP + " value: " + flagValueFromEnvironment); } else { System.out.println("System property " + OgnlRuntime.USE_FIRSTMATCH_GETSET_LOOKUP + " not present. Default value should be: " + defaultValue); } System.out.println("Current OGNL value for Use First Match Get/Set State Flag: " + OgnlRuntime.getUseFirstMatchGetSetLookupValue()); assertEquals(optionDefinedInEnvironment ? flagValueFromEnvironment : defaultValue, OgnlRuntime.getUseFirstMatchGetSetLookupValue(), "Mismatch between system property (or default) and OgnlRuntime _useFirstMatchGetSetLookup flag state ?"); } private final OgnlContext defaultContext = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); @Test // Success public void testForArray() throws Exception { Bean bean = new Bean(); Ognl.setValue("chars", defaultContext, bean, new Character[]{'%', '_'}); assertEquals(2, bean.chars.length); assertEquals('%', bean.chars[0]); assertEquals('_', bean.chars[1]); } @Test // Fail public void testForVarArgs() throws Exception { Bean bean = new Bean(); Ognl.setValue("strings", defaultContext, bean, new String[]{"%", "_"}); assertEquals(2, bean.strings.length); assertEquals("%", bean.strings[0]); assertEquals("_", bean.strings[1]); } static class Bean { private Character[] chars; private Integer index; private String[] strings; @SuppressWarnings("unused") public void setChars(Character[] chars) { this.chars = chars; } @SuppressWarnings("unused") public Character[] getChars() { return chars; } @SuppressWarnings("unused") public void setStrings(String... strings) { this.strings = strings; } @SuppressWarnings("unused") public String[] getStrings() { return strings; } @SuppressWarnings("unused") public void setMix(Integer index, String... strings) { this.index = index; this.strings = strings; } public Integer getIndex() { return index; } } @Test public void shouldInvokeSyntheticBridgeMethod() throws Exception { StringBuilder root = new StringBuilder("abc"); assertEquals((int) 'b', Ognl.getValue("codePointAt(1)", defaultContext, root)); } @Test public void shouldInvokeSuperclassMethod() throws Exception { Map root = Collections.singletonMap(3L, 33L); assertTrue((Boolean) Ognl.getValue("containsKey(3L)", defaultContext, root)); } @Test public void shouldInvokeInterfaceMethod() throws Exception { assertTrue((Boolean) Ognl.getValue("isEmpty()", defaultContext, Collections.checkedCollection(new ArrayList<>(), String.class))); } public interface I1 { @SuppressWarnings("unused") Integer getId(); } public interface I2 { @SuppressWarnings("unused") Integer getId(); } @Test public void shouldMultipleInterfaceWithTheSameMethodBeFine() throws Exception { class C1 implements I1, I2 { public Integer getId() { return 100; } } assertEquals(100, Ognl.getValue("getId()", defaultContext, new C1())); } public interface I3 { T get(); } public interface I4 { Long get(); } @Test public void shouldTwoMethodsWithDifferentReturnTypeBeFine() throws Exception { class C1 implements I3, I4 { @Override public Long get() { return 3L; } } assertEquals(3L, Ognl.getValue("get()", defaultContext, new C1())); } @Test public void shouldSameMethodOfDifferentParentsBeCallable() throws Exception { Map root = new HashMap<>(); root.put("d1", java.sql.Date.valueOf("2022-01-01")); root.put("d2", java.sql.Date.valueOf("2022-01-02")); defaultContext.setRoot(root); assertEquals(-1, Ognl.getValue("d1.compareTo(d2)", defaultContext, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ASTChainTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.IndexedSetObject; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ASTChainTest { @Test void getIndexedValue() throws Exception { OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); IndexedSetObject root = new IndexedSetObject(); String expr = "thing[\"x\"].val"; assertEquals(1, Ognl.getValue(expr, context, root)); Ognl.setValue(expr, context, root, 2); assertEquals(2, Ognl.getValue(expr, context, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ASTMethodTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.ASTConst; import ognl.ASTMethod; import ognl.ASTProperty; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlRuntime; import ognl.SimpleNode; import ognl.enhance.ExpressionCompiler; import ognl.test.objects.Bean2; import ognl.test.objects.Bean3; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class ASTMethodTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); } @Test void contextTypes() { ASTMethod p = new ASTMethod(0); p.setMethodName("get"); ASTConst pRef = new ASTConst(0); pRef.setValue("value"); p.jjtAddChild(pRef, 0); Root root = new Root(); context.setRoot(root.getMap()); context.setCurrentObject(root.getMap()); context.setCurrentType(root.getMap().getClass()); assertEquals(".get(\"value\")", p.toGetSourceString(context, root.getMap())); assertEquals(Object.class, context.getCurrentType()); assertEquals(context.getCurrentObject(), root.getMap().get("value")); assertTrue(Map.class.isAssignableFrom(context.getCurrentAccessor())); assertTrue(Map.class.isAssignableFrom(context.getPreviousType())); assertNull(context.getPreviousAccessor()); assertEquals(".get(\"value\")", OgnlRuntime.getCompiler().castExpression(context, p, ".get(\"value\")")); assertNull(context.get(ExpressionCompiler.PRE_CAST)); // now test one context level further to see casting work properly on base object types ASTProperty prop = new ASTProperty(0); ASTConst propRef = new ASTConst(0); propRef.setValue("bean3"); prop.jjtAddChild(propRef, 0); Bean2 val = (Bean2) root.getMap().get("value"); assertEquals(".getBean3()", prop.toGetSourceString(context, root.getMap().get("value"))); assertEquals(context.getCurrentObject(), val.getBean3()); assertEquals(Bean3.class, context.getCurrentType()); assertEquals(Bean2.class, context.getCurrentAccessor()); assertEquals(Object.class, context.getPreviousType()); assertTrue(Map.class.isAssignableFrom(context.getPreviousAccessor())); assertEquals(").getBean3()", OgnlRuntime.getCompiler().castExpression(context, prop, ".getBean3()")); } @Test void isSimpleMethod() throws Exception { SimpleNode node = (SimpleNode) Ognl.parseExpression("#name"); assertFalse(node.isSimpleMethod(context)); node = (SimpleNode) Ognl.parseExpression("#name.lastChar"); assertFalse(node.isSimpleMethod(context)); node = (SimpleNode) Ognl.parseExpression("execute()"); assertTrue(node.isSimpleMethod(context)); node = (SimpleNode) Ognl.parseExpression("bean.execute()"); assertFalse(node.isSimpleMethod(context)); node = (SimpleNode) Ognl.parseExpression("bean.execute()"); assertFalse(node.isSimpleMethod(context)); node = (SimpleNode) Ognl.parseExpression("{name.lastChar, #boo, foo()}"); assertFalse(node.isSimpleMethod(context)); node = (SimpleNode) Ognl.parseExpression("(name.lastChar, #boo, foo())"); assertFalse(node.isSimpleMethod(context)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ASTPropertyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.ASTChain; import ognl.ASTConst; import ognl.ASTProperty; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlRuntime; import ognl.SimpleNode; import ognl.test.objects.BaseGeneric; import ognl.test.objects.GameGeneric; import ognl.test.objects.GameGenericObject; import ognl.test.objects.GenericRoot; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class ASTPropertyTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); } @Test void test_Get_Indexed_Property_Type() throws Exception { ASTProperty p = new ASTProperty(0); p.setIndexedAccess(false); ASTConst pRef = new ASTConst(0); pRef.setValue("nested"); pRef.jjtSetParent(p); p.jjtAddChild(pRef, 0); Map root = new Root().getMap(); context.setRoot(root); context.setCurrentObject(root); context.setCurrentNode(pRef); assertEquals(root.getClass(), context.getCurrentType()); assertNull(context.getPreviousType()); assertEquals(root, context.getCurrentObject()); assertNull(context.getCurrentAccessor()); assertNull(context.getPreviousAccessor()); int type = p.getIndexedPropertyType(context, root); assertEquals(OgnlRuntime.INDEXED_PROPERTY_NONE, type); assertEquals(root.getClass(), context.getCurrentType()); assertNull(context.getPreviousType()); assertNull(context.getCurrentAccessor()); assertNull(context.getPreviousAccessor()); } @Test void test_Get_Value_Body() throws Exception { ASTProperty p = new ASTProperty(0); p.setIndexedAccess(false); ASTConst pRef = new ASTConst(0); pRef.setValue("nested"); pRef.jjtSetParent(p); p.jjtAddChild(pRef, 0); Map root = new Root().getMap(); context.setRoot(root); context.setCurrentObject(root); context.setCurrentNode(pRef); assertEquals(root.getClass(), context.getCurrentType()); assertNull(context.getPreviousType()); assertEquals(root, context.getCurrentObject()); assertNull(context.getCurrentAccessor()); assertNull(context.getPreviousAccessor()); Object value = p.getValue(context, root); assertEquals(root.get("nested"), value); assertEquals(root.getClass(), context.getCurrentType()); assertNull(context.getPreviousType()); assertNull(context.getCurrentAccessor()); assertNull(context.getPreviousAccessor()); } @Test void test_Get_Source() { ASTProperty p = new ASTProperty(0); p.setIndexedAccess(false); ASTConst pRef = new ASTConst(0); pRef.setValue("nested"); pRef.jjtSetParent(p); p.jjtAddChild(pRef, 0); Map root = new Root().getMap(); context.setRoot(root); context.setCurrentObject(root); context.setCurrentNode(pRef); assertEquals(".get(\"nested\")", p.toGetSourceString(context, root)); assertEquals(Object.class, context.getCurrentType()); assertEquals(Map.class, context.getCurrentAccessor()); assertEquals(root.getClass(), context.getPreviousType()); assertNull(context.getPreviousAccessor()); assertEquals(root.get("nested"), context.getCurrentObject()); assertTrue(Map.class.isAssignableFrom(context.getCurrentAccessor())); assertEquals(root.getClass(), context.getPreviousType()); assertNull(context.getPreviousAccessor()); } @Test void test_Set_Source() { ASTProperty p = new ASTProperty(0); p.setIndexedAccess(false); ASTConst pRef = new ASTConst(0); pRef.setValue("nested"); pRef.jjtSetParent(p); p.jjtAddChild(pRef, 0); Map root = new Root().getMap(); context.setRoot(root); context.setCurrentObject(root); context.setCurrentNode(pRef); assertEquals(".put(\"nested\", $3)", p.toSetSourceString(context, root)); assertEquals(Object.class, context.getCurrentType()); assertEquals(root.get("nested"), context.getCurrentObject()); assertTrue(Map.class.isAssignableFrom(context.getCurrentAccessor())); assertEquals(root.getClass(), context.getPreviousType()); assertNull(context.getPreviousAccessor()); } @Test void test_Indexed_Object_Type() { ASTProperty listp = new ASTProperty(0); listp.setIndexedAccess(false); ASTConst listc = new ASTConst(0); listc.setValue("list"); listc.jjtSetParent(listp); listp.jjtAddChild(listc, 0); ASTProperty p = new ASTProperty(0); p.setIndexedAccess(true); ASTProperty pindex = new ASTProperty(0); ASTConst pRef = new ASTConst(0); pRef.setValue("genericIndex"); pRef.jjtSetParent(pindex); pindex.jjtAddChild(pRef, 0); p.jjtAddChild(pindex, 0); Root root = new Root(); context.setRoot(root); context.setCurrentObject(root); context.setCurrentNode(listp); assertEquals(".getList()", listp.toGetSourceString(context, root)); assertEquals(List.class, context.getCurrentType()); assertEquals(Root.class, context.getCurrentAccessor()); assertNull(context.getPreviousAccessor()); assertEquals(root.getClass(), context.getPreviousType()); assertEquals(root.getList(), context.getCurrentObject()); context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); context.setRoot(root); context.setCurrentObject(root); ASTChain chain = new ASTChain(0); listp.jjtSetParent(chain); chain.jjtAddChild(listp, 0); context.setCurrentNode(chain); assertEquals(".getList()", chain.toGetSourceString(context, root)); assertEquals(List.class, context.getCurrentType()); assertEquals(Root.class, context.getCurrentAccessor()); assertNull(context.getPreviousAccessor()); assertEquals(Root.class, context.getPreviousType()); assertEquals(root.getList(), context.getCurrentObject()); assertEquals(".get(ognl.OgnlOps#getIntValue(((ognl.test.objects.Root)$2)..getGenericIndex().toString()))", p.toGetSourceString(context, root.getList())); assertEquals(root.getArray(), context.getCurrentObject()); assertEquals(Object.class, context.getCurrentType()); } @Test void test_Complicated_List() throws Exception { Root root = new Root(); SimpleNode node = (SimpleNode) Ognl.compileExpression(context, root, "{ new ognl.test.objects.MenuItem('Home', 'Main', " + "{ new ognl.test.objects.MenuItem('Help', 'Help'), " + "new ognl.test.objects.MenuItem('Contact', 'Contact') }), " // end first item + "new ognl.test.objects.MenuItem('UserList', getMessages().getMessage('menu.members')), " + "new ognl.test.objects.MenuItem('account/BetSlipList', getMessages().getMessage('menu.account'), " + "{ new ognl.test.objects.MenuItem('account/BetSlipList', 'My Bets'), " + "new ognl.test.objects.MenuItem('account/TransactionList', 'My Transactions') }), " + "new ognl.test.objects.MenuItem('About', 'About'), " + "new ognl.test.objects.MenuItem('admin/Admin', getMessages().getMessage('menu.admin'), " + "{ new ognl.test.objects.MenuItem('admin/AddEvent', 'Add event'), " + "new ognl.test.objects.MenuItem('admin/AddResult', 'Add result') })}"); assertTrue(List.class.isAssignableFrom(node.getAccessor().get(context, root).getClass())); } @Test void test_Set_Chain_Indexed_Property() throws Exception { Root root = new Root(); context.setRoot(root); context.setCurrentObject(root); SimpleNode node = (SimpleNode) Ognl.parseExpression("tab.searchCriteriaSelections[index1][index2]"); node.setValue(context, root, Boolean.FALSE); assertEquals(Boolean.FALSE, root.getTab().getSearchCriteriaSelections().get(1).get(1)); } @Test void test_Set_Generic_Property() throws Exception { GenericRoot root = new GenericRoot(); context.setRoot(root); context.setCurrentObject(root); SimpleNode node = (SimpleNode) Ognl.parseExpression("cracker.param"); node.setValue(context, root, "0"); assertEquals(0, root.getCracker().getParam()); node.setValue(context, root, "10"); assertEquals(10, root.getCracker().getParam()); } @Test void test_Get_Generic_Property() throws Exception { GenericRoot root = new GenericRoot(); context.setRoot(root); context.setCurrentObject(root); SimpleNode node = (SimpleNode) Ognl.parseExpression("cracker.param"); node.setValue(context, root, "0"); assertEquals(0, node.getValue(context, root)); node.setValue(context, root, "10"); assertEquals(10, node.getValue(context, root)); } @Test void test_Set_Get_Multiple_Generic_Types_Property() throws Exception { BaseGeneric root = new GameGeneric(); context.setRoot(root); context.setCurrentObject(root); SimpleNode node = (SimpleNode) Ognl.parseExpression("ids"); node.setValue(context, root, new String[]{"0", "20", "43"}); assertArrayEquals(new Long[]{0L, 20L, 43L}, root.getIds()); Object actual = node.getValue(context, root); assertArrayEquals((Object[]) actual, root.getIds()); } } ================================================ FILE: ognl/src/test/java/ognl/test/ASTSequenceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.SimpleNode; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ASTSequenceTest { @Test void isSequence() throws Exception { OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); SimpleNode node = (SimpleNode) Ognl.parseExpression("#name"); assertFalse(node.isSequence(context)); node = (SimpleNode) Ognl.parseExpression("#name = 'boo', System.out.println(#name)"); assertTrue(node.isSequence(context)); node = (SimpleNode) Ognl.parseExpression("#name['foo'] = 'bar'"); assertFalse(node.isSequence(context)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ArithmeticAndLogicalOperatorsOnEnumsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class ArithmeticAndLogicalOperatorsOnEnumsTest { private static final String FULLY_QUALIFIED_CLASSNAME = ArithmeticAndLogicalOperatorsOnEnumsTest.class.getName(); private OgnlContext context; private Root root; public enum EnumNoBody {ENUM1, ENUM2;} public enum EnumEmptyBody {ENUM1 {}, ENUM2 {};} public enum EnumBasicBody { ENUM1 { public final Integer value() { return 10; } }, ENUM2 { public final Integer value() { return 20; } }; } @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); context.put("x", "1"); context.put("y", new BigDecimal(1)); root = new Root(); } @Test void enumNoBodyEquality() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1", context, root)); } @Test void enumNoBodyInequality() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1", context, root)); } @Test void enumNoBodyEqualityEnum2() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM2 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM2", context, root)); } @Test void enumNoBodyInequalityEnum2() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM2 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM2", context, root)); } @Test void enumNoBodyDifferentEnums() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM2", context, root)); } @Test void enumNoBodyDifferentEnumsEquality() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM2", context, root)); } @Test void enumEmptyBodyEquality() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1", context, root)); } @Test void enumEmptyBodyInequality() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1", context, root)); } @Test void enumEmptyBodyEqualityEnum2() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM2 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM2", context, root)); } @Test void enumEmptyBodyInequalityEnum2() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM2 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM2", context, root)); } @Test void enumEmptyBodyDifferentEnums() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM2", context, root)); } @Test void enumEmptyBodyDifferentEnumsEquality() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM2", context, root)); } @Test void enumBasicBodyEquality() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1", context, root)); } @Test void enumBasicBodyInequality() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1", context, root)); } @Test void enumBasicBodyEqualityEnum2() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM2 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM2", context, root)); } @Test void enumBasicBodyInequalityEnum2() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM2 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM2", context, root)); } @Test void enumBasicBodyDifferentEnums() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM2", context, root)); } @Test void enumBasicBodyDifferentEnumsEquality() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM2", context, root)); } @Test void enumNoBodyAndEnumEmptyBodyEquality() { assertThrows(IllegalArgumentException.class, () -> { Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1", context, root); }); } @Test void enumNoBodyAndEnumEmptyBodyInequality() { assertThrows(IllegalArgumentException.class, () -> { Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1", context, root); }); } @Test void enumNoBodyAndEnumBasicBodyEquality() { assertThrows(IllegalArgumentException.class, () -> { Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1", context, root); }); } @Test void enumNoBodyAndEnumBasicBodyInequality() { assertThrows(IllegalArgumentException.class, () -> { Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumNoBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1", context, root); }); } @Test void enumEmptyBodyAndEnumBasicBodyEquality() { assertThrows(IllegalArgumentException.class, () -> { Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1 == @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1", context, root); }); } @Test void enumEmptyBodyAndEnumBasicBodyInequality() { assertThrows(IllegalArgumentException.class, () -> { Ognl.getValue("@" + FULLY_QUALIFIED_CLASSNAME + "$EnumEmptyBody@ENUM1 != @" + FULLY_QUALIFIED_CLASSNAME + "$EnumBasicBody@ENUM1", context, root); }); } } ================================================ FILE: ognl/src/test/java/ognl/test/ArithmeticAndLogicalOperatorsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.math.BigInteger; import static org.junit.jupiter.api.Assertions.assertEquals; class ArithmeticAndLogicalOperatorsTest { private OgnlContext context; private Root root; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); context.put("x", "1"); context.put("y", new BigDecimal(1)); root = new Root(); } @Test void doubleValuedArithmeticExpressions() throws OgnlException { assertEquals(-1d, Ognl.getValue("-1d", context, root)); assertEquals(1d, Ognl.getValue("+1d", context, root)); assertEquals(1f, Ognl.getValue("--1f", context, root)); assertEquals(4.0d, Ognl.getValue("2*2.0", context, root)); assertEquals(2.5d, Ognl.getValue("5/2.", context, root)); assertEquals(7d, Ognl.getValue("5+2D", context, root)); assertEquals(3.0f, Ognl.getValue("5f-2F", context, root)); assertEquals(11d, Ognl.getValue("5.+2*3", context, root)); assertEquals(21d, Ognl.getValue("(5.+2)*3", context, root)); } @Test void bigDecimalValuedArithmeticExpressions() throws OgnlException { assertEquals(BigDecimal.valueOf(-1), Ognl.getValue("-1b", context, root)); assertEquals(BigDecimal.valueOf(1), Ognl.getValue("+1b", context, root)); assertEquals(BigDecimal.valueOf(1), Ognl.getValue("--1b", context, root)); assertEquals(BigDecimal.valueOf(4.0), Ognl.getValue("2*2.0b", context, root)); assertEquals(BigDecimal.valueOf(2), Ognl.getValue("5/2.B", context, root)); assertEquals(BigDecimal.valueOf(2.5), Ognl.getValue("5.0B/2", context, root)); assertEquals(BigDecimal.valueOf(7), Ognl.getValue("5+2b", context, root)); assertEquals(BigDecimal.valueOf(3), Ognl.getValue("5-2B", context, root)); assertEquals(BigDecimal.valueOf(11d), Ognl.getValue("5.+2b*3", context, root)); assertEquals(BigDecimal.valueOf(21d), Ognl.getValue("(5.+2b)*3", context, root)); } @Test void integerValuedArithmeticExpressions() throws OgnlException { assertEquals(-1, Ognl.getValue("-1", context, root)); assertEquals(1, Ognl.getValue("+1", context, root)); assertEquals(1, Ognl.getValue("--1", context, root)); assertEquals(4, Ognl.getValue("2*2", context, root)); assertEquals(2, Ognl.getValue("5/2", context, root)); assertEquals(7, Ognl.getValue("5+2", context, root)); assertEquals(3, Ognl.getValue("5-2", context, root)); assertEquals(11, Ognl.getValue("5+2*3", context, root)); assertEquals(21, Ognl.getValue("(5+2)*3", context, root)); assertEquals(~1, Ognl.getValue("~1", context, root)); assertEquals(1, Ognl.getValue("5%2", context, root)); assertEquals(20, Ognl.getValue("5<<2", context, root)); assertEquals(1, Ognl.getValue("5>>2", context, root)); assertEquals(1, Ognl.getValue("5>>1+1", context, root)); assertEquals(-5 >>> 2, Ognl.getValue("-5>>>2", context, root)); assertEquals(-5L >>> 2, Ognl.getValue("-5L>>>2", context, root)); assertEquals(1.0, Ognl.getValue("5. & 3", context, root)); assertEquals(6, Ognl.getValue("5 ^3", context, root)); assertEquals(7L, Ognl.getValue("5l&3|5^3", context, root)); assertEquals(5, Ognl.getValue("5&(3|5^3)", context, root)); assertEquals(1, Ognl.getValue("true ? 1 : 1/0", context, root)); } @Test void bigIntegerValuedArithmeticExpressions() throws OgnlException { assertEquals(BigInteger.valueOf(-1), Ognl.getValue("-1h", context, root)); assertEquals(BigInteger.valueOf(1), Ognl.getValue("+1H", context, root)); assertEquals(BigInteger.valueOf(1), Ognl.getValue("--1h", context, root)); assertEquals(BigInteger.valueOf(4), Ognl.getValue("2h*2", context, root)); assertEquals(BigInteger.valueOf(2), Ognl.getValue("5/2h", context, root)); assertEquals(BigInteger.valueOf(7), Ognl.getValue("5h+2", context, root)); assertEquals(BigInteger.valueOf(3), Ognl.getValue("5-2h", context, root)); assertEquals(BigInteger.valueOf(11), Ognl.getValue("5+2H*3", context, root)); assertEquals(BigInteger.valueOf(21), Ognl.getValue("(5+2H)*3", context, root)); assertEquals(BigInteger.valueOf(~1), Ognl.getValue("~1h", context, root)); assertEquals(BigInteger.valueOf(1), Ognl.getValue("5h%2", context, root)); assertEquals(BigInteger.valueOf(20), Ognl.getValue("5h<<2", context, root)); assertEquals(BigInteger.valueOf(1), Ognl.getValue("5h>>2", context, root)); assertEquals(BigInteger.valueOf(1), Ognl.getValue("5h>>1+1", context, root)); assertEquals(BigInteger.valueOf(-2), Ognl.getValue("-5h>>>2", context, root)); assertEquals(BigInteger.valueOf(1L), Ognl.getValue("5.b & 3", context, root)); assertEquals(BigInteger.valueOf(6), Ognl.getValue("5h ^3", context, root)); assertEquals(BigInteger.valueOf(7L), Ognl.getValue("5h&3|5^3", context, root)); assertEquals(BigInteger.valueOf(5L), Ognl.getValue("5H&(3|5^3)", context, root)); } @Test void logicalExpressions() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("!1", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("!null", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("5<2", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("5>2", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("5<=5", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("5>=3", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("5<-5>>>2", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("5==5.0", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("5!=5.0", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("null in {true,false,null}", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("null not in {true,false,null}", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("null in {true,false,null}.toArray()", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("5 in {true,false,null}", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("5 not in {true,false,null}", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("5 instanceof java.lang.Integer", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("5. instanceof java.lang.Integer", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("!false || true", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("!(true && true)", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("(1 > 0 && true) || 2 > 0", context, root)); } @Test void logicalExpressionsStringVersions() throws OgnlException { assertEquals(Integer.valueOf(2), Ognl.getValue("2 or 0", context, root)); assertEquals(Integer.valueOf(0), Ognl.getValue("1 and 0", context, root)); assertEquals(Integer.valueOf(1), Ognl.getValue("1 bor 0", context, root)); assertEquals(Integer.valueOf(12), Ognl.getValue("true && 12", context, root)); assertEquals(Integer.valueOf(1), Ognl.getValue("1 xor 0", context, root)); assertEquals(0, Ognl.getValue("1 band 0", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("1 eq 1", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("1 neq 1", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("1 lt 5", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("1 lte 5", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("1 gt 5", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("1 gte 5", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("1 lt 5", context, root)); assertEquals(Integer.valueOf(4), Ognl.getValue("1 shl 2", context, root)); assertEquals(Integer.valueOf(1), Ognl.getValue("4 shr 2", context, root)); assertEquals(Integer.valueOf(1), Ognl.getValue("4 ushr 2", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("not null", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("not 1", context, root)); } @Test void equalityOnIdentity() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("#a = new java.lang.Object(), #a == #a", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("#a = new java.lang.Object(), #b = new java.lang.Object(), #a == #b", context, root)); } @Test void comparableAndNonComparable() throws OgnlException { assertEquals(Boolean.FALSE, Ognl.getValue("#a = new java.lang.Object(), #a == ''", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("#a = new java.lang.Object(), '' == #a", context, root)); } @Test void expressionsWithVariables() throws OgnlException { assertEquals(Boolean.TRUE, Ognl.getValue("#x > 0", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("#x < 0", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("#x == 0", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("#x == 1", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("0 > #x", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("0 < #x", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("0 == #x", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("1 == #x", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("\"1\" > 0", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("\"1\" < 0", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("\"1\" == 0", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("\"1\" == 1", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("0 > \"1\"", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("0 < \"1\"", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("0 == \"1\"", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("1 == \"1\"", context, root)); assertEquals("11", Ognl.getValue("#x + 1", context, root)); assertEquals("11", Ognl.getValue("1 + #x", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("#y == 1", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("#y == \"1\"", context, root)); assertEquals("11", Ognl.getValue("#y + \"1\"", context, root)); assertEquals("11", Ognl.getValue("\"1\" + #y", context, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ArrayCreationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.ExpressionSyntaxException; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Entry; import ognl.test.objects.Root; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class ArrayCreationTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); root = new Root(); } @Test void stringArrayCreation() throws Exception { assertArrayEquals(new String[]{"one", "two"}, (String[]) Ognl.getValue("new String[] { \"one\", \"two\" }", context, root)); } @Test void stringArrayWithIntegers() throws Exception { assertArrayEquals(new String[]{"1", "2"}, (String[]) Ognl.getValue("new String[] { 1, 2 }", context, root)); } @Test void testIntegerArrayCreation() throws Exception { assertArrayEquals(new Integer[]{1, 2, 3}, (Integer[]) Ognl.getValue("new Integer[] { 1, 2, 3 }", context, root)); } @Test void stringArrayWithSize() throws Exception { assertArrayEquals(new String[10], (String[]) Ognl.getValue("new String[10]", context, root)); } @Test void invalidObjectArrayCreation() { assertThrows(ExpressionSyntaxException.class, () -> { Ognl.getValue("new Object[4] { #root, #this }", context, root); }); } @Test void objectArrayWithSize() throws Exception { assertArrayEquals(new Object[4], (Object[]) Ognl.getValue("new Object[4]", context, root)); } @Test void objectArrayWithElements() throws Exception { assertArrayEquals(new Object[]{root, root}, (Object[]) Ognl.getValue("new Object[] { #root, #this }", context, root)); } @Test void simpleArrayCreation() throws Exception { assertArrayEquals(new Simple[5], (Simple[]) Ognl.getValue("new ognl.test.objects.Simple[5]", context, root)); } @Test void simpleObjectArrayCreation() throws Exception { assertEquals(new Simple(new Object[5]), Ognl.getValue("new ognl.test.objects.Simple(new Object[5])", context, root)); } @Test void simpleStringArrayCreation() throws Exception { assertEquals(new Simple(new String[5]), Ognl.getValue("new ognl.test.objects.Simple(new String[5])", context, root)); } @Test void conditionalEntryArrayCreation() throws Exception { assertArrayEquals(new Entry[]{new Entry(), new Entry()}, (Entry[]) Ognl.getValue("objectIndex ? new ognl.test.objects.Entry[] { new ognl.test.objects.Entry(), new ognl.test.objects.Entry()} : new ognl.test.objects.Entry[] { new ognl.test.objects.Entry(), new ognl.test.objects.Entry()}", context, root)); } @Test void simpleArrayWithElements() throws Exception { assertArrayEquals(new Simple[]{new Simple(), new Simple("foo", 1.0f, 2)}, (Simple[]) Ognl.getValue("new ognl.test.objects.Simple[] { new ognl.test.objects.Simple(), new ognl.test.objects.Simple(\"foo\", 1.0f, 2) }", context, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ArrayElementsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; class ArrayElementsTest { private Root root; private int[] intArray; private String[] stringArray; private OgnlContext rootContext; private OgnlContext intArrayContext; private OgnlContext stringArrayContext; @BeforeEach void setUp() { root = new Root(); rootContext = Ognl.createDefaultContext(root); intArray = new int[]{10, 20}; intArrayContext = Ognl.createDefaultContext(intArray); stringArray = new String[]{"hello", "world"}; stringArrayContext = Ognl.createDefaultContext(stringArray); } @Test void stringArrayLength() throws OgnlException { assertEquals(2, Ognl.getValue("length", stringArrayContext, stringArray)); } @Test void stringArrayElement() throws OgnlException { assertEquals("world", Ognl.getValue("#root[1]", stringArrayContext, stringArray)); } @Test void intArrayElement() throws OgnlException { assertEquals(20, Ognl.getValue("#root[1]", intArrayContext, intArray)); } @Test void intArrayElementAfterSet() throws OgnlException { Ognl.setValue("#root[1]", intArrayContext, intArray, 50); assertEquals(50, Ognl.getValue("#root[1]", intArrayContext, intArray)); } @Test void intArrayElementAfterSetWithString() throws OgnlException { Ognl.setValue("#root[1]", intArrayContext, intArray, "50"); assertEquals(50, Ognl.getValue("#root[1]", intArrayContext, intArray)); } @Test void rootIntValueAfterSetWithString() throws OgnlException { Ognl.setValue("intValue", rootContext, root, "50"); assertEquals(50, Ognl.getValue("intValue", rootContext, root)); } @Test void rootArrayAfterSetWithStringArray() throws OgnlException { Ognl.setValue("array", rootContext, root, new String[]{"50", "100"}); assertEquals(Arrays.toString(new int[]{50, 100}), Arrays.toString((int[]) Ognl.getValue("array", rootContext, root))); } @Test void charArrayElement() throws OgnlException { assertEquals('}', Ognl.getValue("\"{Hello}\".toCharArray()[6]", rootContext, root)); } @Test void charArrayElementFromString() throws OgnlException { assertEquals('p', Ognl.getValue("\"Tapestry\".toCharArray()[2]", rootContext, root)); } @Test void charArray() throws OgnlException { assertEquals(Arrays.asList('1', '2', '3'), Ognl.getValue("{'1','2','3'}", rootContext, root)); } @Test void booleanArray() throws OgnlException { assertEquals(Arrays.asList(true, true), Ognl.getValue("{ true, !false }", rootContext, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ChainTest.java ================================================ /* * Copyright 2020 OGNL Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.SimpleNode; import ognl.test.objects.Root; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests for {@link SimpleNode#isChain(OgnlContext)}. */ class ChainTest { @Test void test_isChain() throws Exception { OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); SimpleNode node = (SimpleNode) Ognl.parseExpression("#name"); assertFalse(node.isChain(context)); node = (SimpleNode) Ognl.parseExpression("#name.lastChar"); assertTrue(node.isChain(context)); node = (SimpleNode) Ognl.parseExpression("#{name.lastChar, #boo}"); assertTrue(node.isChain(context)); node = (SimpleNode) Ognl.parseExpression("boo = #{name.lastChar, #boo, foo()}"); assertTrue(node.isChain(context)); node = (SimpleNode) Ognl.parseExpression("{name.lastChar, #boo, foo()}"); assertTrue(node.isChain(context)); node = (SimpleNode) Ognl.parseExpression("(name.lastChar, #boo, foo())"); assertTrue(node.isChain(context)); } @Test void shouldShortCircuitAccessingNullChild() throws OgnlException { OgnlContext context = Ognl.createDefaultContext(null); Parent parent = new Parent(new Parent(null)); context.put("parent", parent); assertNull(Ognl.getValue("#parent.child.child.name", context, parent)); } @Test void shouldEvaluateThisProperty() throws OgnlException { Root root = new Root(); OgnlContext context = Ognl.createDefaultContext(root); assertEquals("empty", Ognl.getValue("map[$].(#this == null ? 'empty' : #this)", context, root)); } public static class Child { private final String name; public Child(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return name; } } public static class Parent extends Child { private final Child child; public Parent(Child child) { super("parent of " + child); this.child = child; } public Child getChild() { return child; } } } ================================================ FILE: ognl/src/test/java/ognl/test/ClassMethodTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.CorrectedObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; class ClassMethodTest { private CorrectedObject corrected; private OgnlContext context; @BeforeEach void setUp() { corrected = new CorrectedObject(); context = Ognl.createDefaultContext(corrected, new DefaultMemberAccess(true)); } @Test void testGetClassName() throws Exception { Object actual = Ognl.getValue("getClass().getName()", context, corrected); assertEquals(corrected.getClass().getName(), actual); } @Test void testGetClassInterfaces() throws Exception { Class[] actual = (Class[]) Ognl.getValue("getClass().getInterfaces()", context, corrected); assertArrayEquals(corrected.getClass().getInterfaces(), actual); } @Test void testGetClassInterfacesLength() throws Exception { Object actual = Ognl.getValue("getClass().getInterfaces().length", context, corrected); assertEquals(corrected.getClass().getInterfaces().length, actual); } @Test void testSystemClassGetInterfaces() throws Exception { Class[] actual = (Class[]) Ognl.getValue("@System@class.getInterfaces()", context, corrected); assertArrayEquals(System.class.getInterfaces(), actual); } @Test void testClassGetName() throws Exception { Object actual = Ognl.getValue("@Class@class.getName()", context, corrected); assertEquals(Class.class.getName(), actual); } @Test void testImageObserverClassGetName() throws Exception { Object actual = Ognl.getValue("@java.awt.image.ImageObserver@class.getName()", context, corrected); assertEquals(java.awt.image.ImageObserver.class.getName(), actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/CollectionDirectPropertyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; class CollectionDirectPropertyTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root); } @Test void testSize() throws Exception { assertEquals(2, Ognl.getValue("size", context, Arrays.asList("hello", "world"))); } @Test void testIsEmptyFalse() throws Exception { assertEquals(false, Ognl.getValue("isEmpty", context, Arrays.asList("hello", "world"))); } @Test void testIsEmptyTrue() throws Exception { assertEquals(true, Ognl.getValue("isEmpty", context, Arrays.asList())); } @Test void testIteratorNext() throws Exception { assertEquals("hello", Ognl.getValue("iterator.next", context, Arrays.asList("hello", "world"))); } @Test void testIteratorHasNext() throws Exception { assertEquals(true, Ognl.getValue("iterator.hasNext", context, Arrays.asList("hello", "world"))); } @Test void testIteratorHasNextAfterTwoNexts() throws Exception { assertEquals(false, Ognl.getValue("#it = iterator, #it.next, #it.next, #it.hasNext", context, Arrays.asList("hello", "world"))); } @Test void testIteratorNextAfterTwoNexts() throws Exception { assertEquals("world", Ognl.getValue("#it = iterator, #it.next, #it.next", context, Arrays.asList("hello", "world"))); } @Test void testRootMapTest() throws Exception { assertEquals(root, Ognl.getValue("map[\"test\"]", context, root)); } @Test void testRootMapSize() throws Exception { assertEquals(root.getMap().size(), Ognl.getValue("map.size", context, root)); } @Test void testRootMapKeySet() throws Exception { assertEquals(root.getMap().keySet(), Ognl.getValue("map.keySet", context, root)); } @Test void testRootMapValues() throws Exception { assertEquals(root.getMap().values(), Ognl.getValue("map.values", context, root)); } @Test void testRootMapKeysSize() throws Exception { assertEquals(root.getMap().keySet().size(), Ognl.getValue("map.keys.size", context, root)); } @Test void testRootMapSizeValue() throws Exception { assertEquals(root.getMap().get("size"), Ognl.getValue("map[\"size\"]", context, root)); } @Test void testRootMapIsEmpty() throws Exception { assertEquals(root.getMap().isEmpty() ? Boolean.TRUE : Boolean.FALSE, Ognl.getValue("map.isEmpty", context, root)); } @Test void testRootMapIsEmptyKey() throws Exception { assertEquals(null, Ognl.getValue("map[\"isEmpty\"]", context, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/CompilingPropertyAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.LoaderClassPath; import ognl.ClassResolver; import ognl.ObjectPropertyAccessor; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import ognl.enhance.ContextClassLoader; import ognl.enhance.EnhancedClassLoader; import ognl.test.util.NameFactory; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; /** * Implementation of PropertyAccessor that uses Javassist to compile a property accessor * specifically tailored to the property. */ @Deprecated(since = "3.5.0", forRemoval = true) public class CompilingPropertyAccessor extends ObjectPropertyAccessor { private static final NameFactory NAME_FACTORY = new NameFactory("ognl.PropertyAccessor", "v"); private static final Getter NotFoundGetter = (context, target, propertyName) -> null; private static final Getter DefaultGetter = (context, target, propertyName) -> { try { return OgnlRuntime.getMethodValue(context, target, propertyName, true); } catch (Exception ex) { throw new RuntimeException(ex); } }; private static final Map pools = new HashMap<>(); private static final Map loaders = new HashMap<>(); private static final Map, Class> PRIMITIVE_WRAPPER_CLASSES = new IdentityHashMap<>(); private final Map, Map> seenGetMethods = new IdentityHashMap<>(); static { PRIMITIVE_WRAPPER_CLASSES.put(Boolean.TYPE, Boolean.class); PRIMITIVE_WRAPPER_CLASSES.put(Boolean.class, Boolean.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Byte.TYPE, Byte.class); PRIMITIVE_WRAPPER_CLASSES.put(Byte.class, Byte.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Character.TYPE, Character.class); PRIMITIVE_WRAPPER_CLASSES.put(Character.class, Character.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Short.TYPE, Short.class); PRIMITIVE_WRAPPER_CLASSES.put(Short.class, Short.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Integer.TYPE, Integer.class); PRIMITIVE_WRAPPER_CLASSES.put(Integer.class, Integer.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Long.TYPE, Long.class); PRIMITIVE_WRAPPER_CLASSES.put(Long.class, Long.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Float.TYPE, Float.class); PRIMITIVE_WRAPPER_CLASSES.put(Float.class, Float.TYPE); PRIMITIVE_WRAPPER_CLASSES.put(Double.TYPE, Double.class); PRIMITIVE_WRAPPER_CLASSES.put(Double.class, Double.TYPE); } public static Class getPrimitiveWrapperClass(Class primitiveClass) { return PRIMITIVE_WRAPPER_CLASSES.get(primitiveClass); } public interface Getter { Object get(OgnlContext context, Object target, String propertyName); } public static Getter generateGetter(OgnlContext context, String code) throws OgnlException { String className = NAME_FACTORY.getNewClassName(); try { ClassPool pool = pools.get(context.getClassResolver()); EnhancedClassLoader loader = loaders.get(context.getClassResolver()); CtClass newClass; CtClass ognlContextClass; CtClass objectClass; CtClass stringClass; CtMethod method; byte[] byteCode; Class compiledClass; if ((pool == null) || (loader == null)) { ClassLoader classLoader = new ContextClassLoader(OgnlContext.class.getClassLoader(), context); pool = ClassPool.getDefault(); pool.insertClassPath(new LoaderClassPath(classLoader)); pools.put(context.getClassResolver(), pool); loader = new EnhancedClassLoader(classLoader); loaders.put(context.getClassResolver(), loader); } newClass = pool.makeClass(className); ognlContextClass = pool.get(OgnlContext.class.getName()); objectClass = pool.get(Object.class.getName()); stringClass = pool.get(String.class.getName()); newClass.addInterface(pool.get(Getter.class.getName())); method = new CtMethod(objectClass, "get", new CtClass[]{ognlContextClass, objectClass, stringClass}, newClass); method.setBody("{" + code + "}"); newClass.addMethod(method); byteCode = newClass.toBytecode(); compiledClass = loader.defineClass(className, byteCode); return (Getter) compiledClass.newInstance(); } catch (Throwable ex) { throw new OgnlException("Cannot create class", ex); } } private Getter getGetter(OgnlContext context, Object target, String propertyName) throws OgnlException { Getter result; Class targetClass = target.getClass(); Map propertyMap; if ((propertyMap = seenGetMethods.get(targetClass)) == null) { propertyMap = new HashMap<>(101); seenGetMethods.put(targetClass, propertyMap); } if ((result = propertyMap.get(propertyName)) == null) { try { Method method = OgnlRuntime.getGetMethod(targetClass, propertyName); if (method != null) { if (Modifier.isPublic(method.getModifiers())) { if (method.getReturnType().isPrimitive()) { propertyMap.put(propertyName, result = generateGetter(context, "java.lang.Object\t\tresult;\n" + targetClass.getName() + "\t" + "t0 = (" + targetClass.getName() + ")$2;\n" + "\n" + "try {\n" + " result = new " + getPrimitiveWrapperClass(method.getReturnType()).getName() + "(t0." + method.getName() + "());\n" + "} catch (java.lang.Exception ex) {\n" + " throw new java.lang.RuntimeException(ex);\n" + "}\n" + "return result;")); } else { propertyMap.put(propertyName, result = generateGetter(context, "java.lang.Object\t\tresult;\n" + targetClass.getName() + "\t" + "t0 = (" + targetClass.getName() + ")$2;\n" + "\n" + "try {\n" + " result = t0." + method.getName() + "();\n" + "} catch (java.lang.Exception ex) {\n" + " throw new java.lang.RuntimeException(ex);\n" + "}\n" + "return result;")); } } else { propertyMap.put(propertyName, result = DefaultGetter); } } else { propertyMap.put(propertyName, result = NotFoundGetter); } } catch (Exception ex) { throw new OgnlException("getting getter", ex); } } return result; } /** * Returns OgnlRuntime.NotFound if the property does not exist. */ public Object getPossibleProperty(OgnlContext context, Object target, String name) throws OgnlException { Object result; if (context.get("_compile") != null) { Getter getter = getGetter(context, target, name); if (getter != NotFoundGetter) { result = getter.get(context, target, name); } else { try { result = OgnlRuntime.getFieldValue(context, target, name, true); } catch (Exception ex) { throw new OgnlException(name, ex); } } } else { result = super.getPossibleProperty(context, target, name); } return result; } } ================================================ FILE: ognl/src/test/java/ognl/test/ConstantTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.ExpressionSyntaxException; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; class ConstantTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root); } @Test void test12345() throws Exception { Object actual = Ognl.getValue("12345", context, root); assertEquals(12345, actual); } @Test void test0x100() throws Exception { Object actual = Ognl.getValue("0x100", context, root); assertEquals(256, actual); } @Test void test0xfE() throws Exception { Object actual = Ognl.getValue("0xfE", context, root); assertEquals(254, actual); } @Test void test01000() throws Exception { assertEquals(512, Ognl.getValue("01000", context, root)); } @Test void test1234L() throws Exception { Object actual = Ognl.getValue("1234L", context, root); assertEquals(1234L, actual); } @Test void test12_34() throws Exception { Object actual = Ognl.getValue("12.34", context, root); assertEquals(12.34, actual); } @Test void test_1234() throws Exception { Object actual = Ognl.getValue(".1234", context, root); assertEquals(0.1234, actual); } @Test void test12_34f() throws Exception { Object actual = Ognl.getValue("12.34f", context, root); assertEquals(12.34F, actual); } @Test void test12_() throws Exception { Object actual = Ognl.getValue("12.", context, root); assertEquals(12.0, actual); } @Test void test12e_1d() throws Exception { Object actual = Ognl.getValue("12e+1d", context, root); assertEquals(120.0, actual); } @Test void test_x() throws Exception { Object actual = Ognl.getValue("'x'", context, root); assertEquals('x', actual); } @Test void test_n() throws Exception { Object actual = Ognl.getValue("'\\n'", context, root); assertEquals('\n', actual); } @Test void test_u048c() throws Exception { Object actual = Ognl.getValue("'\\u048c'", context, root); assertEquals('\u048c', actual); } @Test void test_47() throws Exception { Object actual = Ognl.getValue("'\\47'", context, root); assertEquals('\47', actual); } @Test void test_367() throws Exception { Object actual = Ognl.getValue("'\\367'", context, root); assertEquals('\367', actual); } @Test void test_367Exception() { assertThrows(ExpressionSyntaxException.class, () -> Ognl.getValue("'\\367", context, root), "Invalid octal escape sequence"); } @Test void test_xException() { assertThrows(ExpressionSyntaxException.class, () -> Ognl.getValue("'\\x'", context, root), "Invalid hexadecimal escape sequence"); } @Test void testHelloWorld() throws Exception { Object actual = Ognl.getValue("\"hello world\"", context, root); assertEquals("hello world", actual); } @Test void testUnicodeString() throws Exception { Object actual = Ognl.getValue("\"\\u00a0\\u0068ell\\'o\\\\\\n\\r\\f\\t\\b\\\"\\167orld\\\"\"", context, root); assertEquals("\u00a0hell'o\\\n\r\f\t\b\"world\"", actual); } @Test void testHelloWorldException() { assertThrows(ExpressionSyntaxException.class, () -> Ognl.getValue("\"hello world", context, root), "Unterminated string"); } @Test void testHelloXWorldException() { assertThrows(ExpressionSyntaxException.class, () -> Ognl.getValue("\"hello\\x world\"", context, root), "Invalid escape sequence"); } @Test void testNull() throws Exception { Object actual = Ognl.getValue("null", context, root); assertNull(actual); } @Test void testTrue() throws Exception { Object actual = Ognl.getValue("true", context, root); assertEquals(true, actual); } @Test void testFalse() throws Exception { Object actual = Ognl.getValue("false", context, root); assertEquals(false, actual); } @Test void testArray() throws Exception { Object actual = Ognl.getValue("{ false, true, null, 0, 1. }", context, root); assertEquals(Arrays.asList(false, true, null, 0, 1.0), actual); } @Test void testHtmlPublic() throws Exception { Object actual = Ognl.getValue("'HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"'", context, root); assertEquals("HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"", actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/ConstantTreeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ConstantTreeTest { /** * Field used in test */ public static int nonFinalStaticVariable = 15; private OgnlContext context; @BeforeEach void setUp() { Root root = new Root(); context = Ognl.createDefaultContext(root); } @Test void testTrue() throws Exception { assertTrue(Ognl.isConstant("true", context)); } @Test void test55() throws Exception { assertTrue(Ognl.isConstant("55", context)); } @Test void testJavaAwtColorBlack() throws Exception { assertTrue(Ognl.isConstant("@java.awt.Color@black", context)); } @Test void testNonFinalStaticVariable() throws Exception { assertFalse(Ognl.isConstant("@ognl.test.ConstantTreeTest@nonFinalStaticVariable", context)); } @Test void testNonFinalStaticVariablePlus10() throws Exception { assertFalse(Ognl.isConstant("@ognl.test.ConstantTreeTest@nonFinalStaticVariable + 10", context)); } @Test void test55Plus24PlusJavaAwtEventAltMask() throws Exception { assertTrue(Ognl.isConstant("55 + 24 + @java.awt.Event@ALT_MASK", context)); } @Test void testName() throws Exception { assertFalse(Ognl.isConstant("name", context)); } @Test void testNameI() throws Exception { assertFalse(Ognl.isConstant("name[i]", context)); } @Test void testNameIProperty() throws Exception { assertFalse(Ognl.isConstant("name[i].property", context)); } @Test void testNameFoo() throws Exception { assertFalse(Ognl.isConstant("name.{? foo }", context)); } @Test void testNameFoo2() throws Exception { assertFalse(Ognl.isConstant("name.{ foo }", context)); } @Test void testName25() throws Exception { assertFalse(Ognl.isConstant("name.{ 25 }", context)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ContextRootPreservationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.*; /** * Test case for GitHub issue #390: Context root preservation when using ObjectMethodAccessor on lists */ public class ContextRootPreservationTest { private OgnlContext context; private TestRootObject rootObject; public static class TestRootObject { private String contextProperty = "originalValue"; private List testList = Arrays.asList("item1", "item2", "item3"); public String getContextProperty() { return contextProperty; } public void setContextProperty(String contextProperty) { this.contextProperty = contextProperty; } public List getTestList() { return testList; } public void setTestList(List testList) { this.testList = testList; } } @BeforeEach void setUp() { rootObject = new TestRootObject(); context = Ognl.createDefaultContext(rootObject); } @Test void testContextRootPreservationWithListSelection() throws OgnlException { // This test reproduces the issue described in GitHub issue #390 // When processing a list with selection, the root context should be preserved // allowing access to #root.contextProperty // This should work: accessing context property from root during list processing String expression = "testList.{? #root.contextProperty == 'originalValue'}"; Object result = Ognl.getValue(expression, context, rootObject); // Should return the full list since contextProperty equals 'originalValue' assertNotNull(result); assertTrue(result instanceof List); List resultList = (List) result; assertEquals(3, resultList.size()); assertEquals("item1", resultList.get(0)); assertEquals("item2", resultList.get(1)); assertEquals("item3", resultList.get(2)); } @Test void testIssue390ReproduceBug() throws OgnlException { // This test directly reproduces the bug described in issue #390 // The problem occurs when getValue is called with list items as root, // which overwrites the original context root try { // This expression should work but fails when root context is overwritten String expression = "testList.{? #root.contextProperty != null && #this.startsWith('item')}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertTrue(result instanceof List); List resultList = (List) result; assertEquals(3, resultList.size()); } catch (ognl.NoSuchPropertyException e) { // This demonstrates the issue - the root was replaced with String (list item) assertTrue(e.getMessage().contains("contextProperty")); // The error message shows that contextProperty was looked for on java.lang.String // instead of on the original TestRootObject } } @Test void testContextRootPreservationWithListProjection() throws OgnlException { // Test that root context is preserved during projection operations String expression = "testList.{#root.contextProperty + '_' + #this}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertTrue(result instanceof List); List resultList = (List) result; assertEquals(3, resultList.size()); assertEquals("originalValue_item1", resultList.get(0)); assertEquals("originalValue_item2", resultList.get(1)); assertEquals("originalValue_item3", resultList.get(2)); } @Test void testContextRootPreservationWithNestedExpression() throws OgnlException { // Test that root context is preserved in nested expressions String expression = "#root.contextProperty + ' processed ' + testList.size()"; Object result = Ognl.getValue(expression, context, rootObject); assertEquals("originalValue processed 3", result); } @Test void testContextRootAccessInLambdaExpression() throws OgnlException { // Test that lambda expressions can access the original root context // This test demonstrates the issue - it should pass but currently fails try { String expression = "#filter = :[#root.contextProperty != null ? #this : null], testList.{? #filter(#this) != null}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertTrue(result instanceof List); List resultList = (List) result; assertEquals(3, resultList.size()); } catch (ognl.NoSuchPropertyException e) { // This exception demonstrates the issue: root context is being overwritten assertTrue(e.getMessage().contains("contextProperty")); assertTrue(e.getMessage().contains("java.lang.String")); // Root was replaced with String } } @Test void testRootContextNotOverwrittenByListItem() throws OgnlException { // This test specifically checks that the root context is not overwritten // by the current list item during processing String expression = "testList.{? #root.class.simpleName == 'TestRootObject'}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertTrue(result instanceof List); List resultList = (List) result; assertEquals(3, resultList.size()); // All items should match since root is preserved } @Test void testOriginalRootAccessAfterListProcessing() throws OgnlException { // Test that after list processing, the original root context is still accessible context.put("tempVar", "temp"); String expression = "testList.{? #this.length() > 0}, #root.contextProperty"; Object result = Ognl.getValue(expression, context, rootObject); // The result should be the contextProperty value, not the last list item assertEquals("originalValue", result); } @Test void testContextVariableAccessDuringListProcessing() throws OgnlException { // Test that context variables are accessible during list processing context.put("filterValue", "item2"); String expression = "testList.{? #this == #filterValue}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertTrue(result instanceof List); List resultList = (List) result; assertEquals(1, resultList.size()); assertEquals("item2", resultList.get(0)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ContextVariableTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ContextVariableTest { private Simple root; private OgnlContext context; @BeforeEach void setUp() { root = new Simple(); context = Ognl.createDefaultContext(root); } @Test void testRoot() throws Exception { Object actual = Ognl.getValue("#root", context, root); assertEquals(root, actual); } @Test void testThis() throws Exception { Object actual = Ognl.getValue("#this", context, root); assertEquals(root, actual); } @Test void testSumOfFiveAndSix() throws Exception { Object actual = Ognl.getValue("#f=5, #s=6, #f + #s", context, root); assertEquals(11, actual); } @Test void testSumOfFiveAndSixWithIntermediateAssignment() throws Exception { Object actual = Ognl.getValue("#six=(#five=5, 6), #five + #six", context, root); assertEquals(11, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/CorrectedObjectNullHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.NullHandler; import ognl.OgnlContext; public class CorrectedObjectNullHandler implements NullHandler { private final String defaultValue; public CorrectedObjectNullHandler(String defaultValue) { super(); this.defaultValue = defaultValue; } public Object nullMethodResult(OgnlContext context, Object target, String methodName, Object[] args) { if (methodName.equals("getStringValue")) { return defaultValue; } return null; } public Object nullPropertyValue(OgnlContext context, Object target, Object property) { if (property.equals("stringValue")) { return defaultValue; } return null; } } ================================================ FILE: ognl/src/test/java/ognl/test/DefaultClassResolverTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultClassResolver; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; class DefaultClassResolverTest { @Test void testClassInDefaultPackageResolution() throws Exception { DefaultClassResolver resolver = new DefaultClassResolver(); OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); assertNotNull(resolver.classForName("ClassInDefaultPackage", context)); } @Test void testEnsureClassNotFoundException() { DefaultClassResolver resolver = new DefaultClassResolver(); OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); try { resolver.classForName("no.such.Class", context); fail("Expected ClassNotFoundException as the specified class does not exist."); } catch (Exception e) { assertEquals(ClassNotFoundException.class, e.getClass()); assertEquals("no.such.Class", e.getMessage()); } } @Test void testEnsureClassNotFoundExceptionReportsSpecifiedName() { DefaultClassResolver resolver = new DefaultClassResolver(); OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); try { resolver.classForName("BogusClass", context); fail("Expected ClassNotFoundException as the specified class does not exist."); } catch (Exception e) { assertEquals(ClassNotFoundException.class, e.getClass()); assertEquals("BogusClass", e.getMessage()); } } } ================================================ FILE: ognl/src/test/java/ognl/test/DualModeEvaluationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Node; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import ognl.test.objects.BaseGeneric; import ognl.test.objects.Bean1; import ognl.test.objects.BeanProvider; import ognl.test.objects.BeanProviderAccessor; import ognl.test.objects.EvenOdd; import ognl.test.objects.GameGeneric; import ognl.test.objects.GameGenericObject; import ognl.test.objects.Indexed; import ognl.test.objects.IndexedSetObject; import ognl.test.objects.ListSourceImpl; import ognl.test.objects.Root; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests that interpreted and compiled evaluation modes produce identical results. * Addresses Issue #18. * *

Tests marked {@code @Disabled} document known divergences between the interpreted and compiled * evaluation paths. The compiler generates Java source code (via javassist), which cannot represent * BigDecimal/BigInteger arithmetic using operators, and has other limitations around instanceof * and method calls on auto-boxed primitives.

*/ class DualModeEvaluationTest { private OgnlContext context; private Root root; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root, new DefaultMemberAccess(false)); } private Object getValueInterpreted(String expression) throws OgnlException { Object tree = Ognl.parseExpression(expression); return ((Node) tree).getValue(context.withRoot(root), root); } @SuppressWarnings("unchecked") private Object getValueCompiled(String expression, OgnlContext ctx) throws Exception { Node node = Ognl.compileExpression(ctx, root, expression); return node.getAccessor().get(ctx, root); } private OgnlContext freshCompiledContext() { OgnlContext ctx = Ognl.createDefaultContext(root, context.getMemberAccess()); ctx.setValues(context.getValues()); return ctx; } private void assertBothModes(String expression, Object expected) throws Exception { assertEquals(expected, getValueInterpreted(expression), "Interpreted mode failed for: " + expression); assertEquals(expected, getValueCompiled(expression, freshCompiledContext()), "Compiled mode failed for: " + expression); } private void assertBothModesMatch(String expression) throws Exception { Object interpreted = getValueInterpreted(expression); Object compiled = getValueCompiled(expression, freshCompiledContext()); assertEquals(interpreted, compiled, "Modes diverged for: " + expression + "\n interpreted: " + interpreted + " (" + (interpreted != null ? interpreted.getClass().getName() : "null") + ")" + "\n compiled: " + compiled + " (" + (compiled != null ? compiled.getClass().getName() : "null") + ")"); } private void setValueInterpreted(String expression, Object value) throws OgnlException { Object tree = Ognl.parseExpression(expression); ((Node) tree).setValue(context.withRoot(root), root, value); } private void setValueCompiled(String expression, Object value, OgnlContext ctx) throws Exception { Node node = Ognl.compileExpression(ctx, root, expression); node.getAccessor().set(ctx, root, value); } private void assertSetThenGetBothModes(String expression, Object setValue, Object expectedGet) throws Exception { // Interpreted: set then get setValueInterpreted(expression, setValue); Object interpretedResult = getValueInterpreted(expression); assertEquals(expectedGet, interpretedResult, "Interpreted set+get failed for: " + expression); // Compiled: set then get (fresh context each time) OgnlContext compiledCtx = freshCompiledContext(); setValueCompiled(expression, setValue, compiledCtx); OgnlContext compiledCtx2 = freshCompiledContext(); Object compiledResult = getValueCompiled(expression, compiledCtx2); assertEquals(expectedGet, compiledResult, "Compiled set+get failed for: " + expression); } /** * These constant tests intentionally overlap with {@code ConstantTest} — that class only exercises * the interpreted path, while these validate that the compiled path produces identical results. */ @Nested class Constants { @Test void integerConstant() throws Exception { assertBothModes("12345", 12345); } @Test void longConstant() throws Exception { assertBothModes("1234L", 1234L); } @Test void doubleConstant() throws Exception { assertBothModes("12.34", 12.34); } @Test void floatConstant() throws Exception { assertBothModes("12.34f", 12.34F); } @Test void trueConstant() throws Exception { assertBothModes("true", Boolean.TRUE); } @Test void falseConstant() throws Exception { assertBothModes("false", Boolean.FALSE); } @Test void nullConstant() throws Exception { assertBothModes("null", null); } @Test void stringConstant() throws Exception { assertBothModes("\"hello world\"", "hello world"); } @Test void charConstant() throws Exception { assertBothModes("'x'", 'x'); } @Test void hexConstant() throws Exception { assertBothModes("0x100", 256); } @Test void octalConstant() throws Exception { assertBothModes("01000", 512); } } @Nested @Disabled("Compiler does not support BigDecimal: Java operators (+, -, *, /) cannot be applied to BigDecimal objects in generated source") class BigDecimalArithmetic { @Test void negation() throws Exception { assertBothModes("-1b", BigDecimal.valueOf(-1)); } @Test void unaryPlus() throws Exception { assertBothModes("+1b", BigDecimal.valueOf(1)); } @Test void doubleNegation() throws Exception { assertBothModes("--1b", BigDecimal.valueOf(1)); } @Test void multiplyWithDouble() throws Exception { assertBothModes("2*2.0b", BigDecimal.valueOf(4.0)); } @Test void divideWithBigDecimalSuffix() throws Exception { assertBothModes("5/2.B", BigDecimal.valueOf(2)); } @Test void divideWithBigDecimalNumerator() throws Exception { assertBothModes("5.0B/2", BigDecimal.valueOf(2.5)); } @Test void addition() throws Exception { assertBothModes("5+2b", BigDecimal.valueOf(7)); } @Test void subtraction() throws Exception { assertBothModes("5-2B", BigDecimal.valueOf(3)); } } @Nested @Disabled("Compiler does not support BigInteger: Java operators cannot be applied to BigInteger objects in generated source") class BigIntegerArithmetic { @Test void negation() throws Exception { assertBothModes("-1h", BigInteger.valueOf(-1)); } @Test void unaryPlus() throws Exception { assertBothModes("+1H", BigInteger.valueOf(1)); } @Test void doubleNegation() throws Exception { assertBothModes("--1h", BigInteger.valueOf(1)); } @Test void multiply() throws Exception { assertBothModes("2h*2", BigInteger.valueOf(4)); } @Test void divide() throws Exception { assertBothModes("5/2h", BigInteger.valueOf(2)); } @Test void addition() throws Exception { assertBothModes("5h+2", BigInteger.valueOf(7)); } @Test void subtraction() throws Exception { assertBothModes("5-2h", BigInteger.valueOf(3)); } @Test void modulus() throws Exception { assertBothModes("5h%2", BigInteger.valueOf(1)); } } @Nested class IntegerArithmetic { @Test void negation() throws Exception { assertBothModes("-1", -1); } @Test void unaryPlus() throws Exception { assertBothModes("+1", 1); } @Test void doubleNegation() throws Exception { assertBothModes("--1", 1); } @Test void multiply() throws Exception { assertBothModes("2*2", 4); } @Test void divide() throws Exception { assertBothModes("5/2", 2); } @Test void addition() throws Exception { assertBothModes("5+2", 7); } @Test void subtraction() throws Exception { assertBothModes("5-2", 3); } @Test void precedence() throws Exception { assertBothModes("5+2*3", 11); } @Test void parentheses() throws Exception { assertBothModes("(5+2)*3", 21); } @Test void modulus() throws Exception { assertBothModes("5%2", 1); } } @Nested class DoubleArithmetic { @Test void negation() throws Exception { assertBothModes("-1d", -1d); } @Test void unaryPlus() throws Exception { assertBothModes("+1d", 1d); } @Test void doubleNegation() throws Exception { assertBothModes("--1d", 1d); } @Test void multiplyWithDouble() throws Exception { assertBothModes("2*2.0", 4.0d); } @Test void divideWithTrailingDot() throws Exception { assertBothModes("5/2.", 2.5d); } @Test void addition() throws Exception { assertBothModes("5+2D", 7d); } @Test void floatSubtraction() throws Exception { assertBothModes("5f-2F", 3.0f); } } @Nested class BitwiseOperations { @Test void bitwiseNot() throws Exception { assertBothModes("~1", ~1); } @Test void leftShift() throws Exception { assertBothModes("5<<2", 20); } @Test void rightShift() throws Exception { assertBothModes("5>>2", 1); } @Test void unsignedRightShift() throws Exception { assertBothModes("-5>>>2", -5 >>> 2); } @Disabled("Compiler loses double type in bitwise AND, returns int instead of double") @Test void bitwiseAndWithDouble() throws Exception { assertBothModes("5. & 3", 1.0); } @Test void bitwiseXor() throws Exception { assertBothModes("5 ^3", 6); } @Test void bitwiseOrWithLong() throws Exception { assertBothModes("5l&3|5^3", 7L); } @Disabled("Compiler widens int to long in grouped bitwise expression") @Test void bitwiseAndGrouped() throws Exception { assertBothModes("5&(3|5^3)", 5); } @Disabled("Compiler does not support BigInteger bitwise operations") @Test void bigIntegerBitwiseNot() throws Exception { assertBothModes("~1h", BigInteger.valueOf(~1)); } @Disabled("Compiler does not support BigInteger bitwise operations") @Test void bigIntegerLeftShift() throws Exception { assertBothModes("5h<<2", BigInteger.valueOf(20)); } @Disabled("Compiler does not support BigInteger bitwise operations") @Test void bigIntegerRightShift() throws Exception { assertBothModes("5h>>2", BigInteger.valueOf(1)); } } @Nested class LogicalExpressions { @Test void logicalNot() throws Exception { assertBothModes("!1", Boolean.FALSE); } @Test void logicalNotNull() throws Exception { assertBothModes("!null", Boolean.TRUE); } @Test void lessThan() throws Exception { assertBothModes("5<2", Boolean.FALSE); } @Test void greaterThan() throws Exception { assertBothModes("5>2", Boolean.TRUE); } @Test void lessOrEqual() throws Exception { assertBothModes("5<=5", Boolean.TRUE); } @Test void greaterOrEqual() throws Exception { assertBothModes("5>=3", Boolean.TRUE); } @Test void equality() throws Exception { assertBothModes("5==5.0", Boolean.TRUE); } @Test void inequality() throws Exception { assertBothModes("5!=5.0", Boolean.FALSE); } @Test void ternary() throws Exception { assertBothModes("true ? 1 : 0", 1); } @Test void ternaryShortCircuitsOnTrueBranch() throws Exception { assertBothModes("true ? 1 : 1/0", 1); } @Test void logicalAndShortCircuits() throws Exception { assertBothModes("false && 1/0 == 0", Boolean.FALSE); } @Test void logicalOrShortCircuits() throws Exception { assertBothModes("true || 1/0 == 0", Boolean.TRUE); } } @Nested class PropertyAccess { @Test void simpleProperty() throws Exception { assertBothModesMatch("index"); } @Test void nestedProperty() throws Exception { assertBothModesMatch("bean2.id"); } @Test void arrayLength() throws Exception { assertBothModesMatch("array.length"); } @Test void arrayIndex() throws Exception { assertBothModesMatch("array[0]"); } @Test void nullObject() throws Exception { assertBothModes("nullObject", null); } @Test void booleanProperty() throws Exception { assertBothModesMatch("disabled"); } @Test void intProperty() throws Exception { assertBothModesMatch("anotherIntValue"); } @Test void staticField() throws Exception { assertBothModesMatch("@ognl.test.objects.Root@STATIC_INT"); } @Test void mapProperty() throws Exception { assertBothModesMatch("map"); } @Test void mapDotAccess() throws Exception { assertBothModesMatch("map.test"); } @Test void mapBracketAccess() throws Exception { assertBothModesMatch("map[\"test\"]"); } @Test void mapConcatKeyAccess() throws Exception { assertBothModesMatch("map[\"te\" + \"st\"]"); } @Test void negatedBooleanProperty() throws Exception { assertBothModes("! booleanValue", Boolean.TRUE); } @Test void negatedBeanProperty() throws Exception { assertBothModes("!bean2.pageBreakAfter", Boolean.TRUE); } @Test void stringLengthCheck() throws Exception { assertBothModesMatch("indexedStringValue != null && indexedStringValue.length() > 0"); } @Test void disabledProperty() throws Exception { assertBothModes("disabled", Boolean.TRUE); } @Test void ternaryWithBooleanProperty() throws Exception { assertBothModes("disabled ? 'disabled' : 'othernot'", "disabled"); } @Test void nullOrBooleanExpression() throws Exception { assertBothModes("nullObject || !readonly", Boolean.TRUE); } @Test void disabledOrReadonly() throws Exception { assertBothModes("disabled || readonly", Boolean.TRUE); } @Test void renderNavigationTernary() throws Exception { assertBothModes("renderNavigation ? '' : 'noborder'", "noborder"); } @Test void stringConcatenationWithProperty() throws Exception { assertBothModes("\"background-color:blue; width:\" + (currentLocaleVerbosity / 2) + \"px\"", "background-color:blue; width:43px"); } } @Nested class MethodCalls { @Test void noArgMethod() throws Exception { assertBothModesMatch("getIndex()"); } @Test void stringArgMethod() throws Exception { assertBothModes("getCurrentClass(\"Test\")", "Test stop"); } @Disabled("Compiler generates invalid cast for method call on auto-boxed primitive") @Test void toStringOnInteger() throws Exception { assertBothModes("index.toString()", "1"); } @Test void staticMethod() throws Exception { assertBothModesMatch("@ognl.test.objects.Root@getStaticInt()"); } } @Nested class NullHandling { @Test void nullEqualityToNull() throws Exception { assertBothModes("null == null", Boolean.TRUE); } @Test void nullInequalityToValue() throws Exception { assertBothModes("null != 1", Boolean.TRUE); } } @Nested class InstanceOf { @Test void integerInstanceOf() throws Exception { assertBothModes("5 instanceof java.lang.Integer", Boolean.TRUE); } @Test void doubleNotInstanceOfInteger() throws Exception { assertBothModes("5. instanceof java.lang.Integer", Boolean.FALSE); } } @Nested class SetterPaths { @Test void setMapNewValue() throws Exception { assertSetThenGetBothModes("map.newValue", 101, 101); } @Test void setMapBracketKey() throws Exception { assertSetThenGetBothModes("map[\"testKey\"]", "testVal", "testVal"); } @Test void setSettableListIndex() throws Exception { assertSetThenGetBothModes("settableList[0]", "foo", "foo"); } @Test void setIntValueProperty() throws Exception { assertSetThenGetBothModes("intValue", 42, 42); } @Test void setOpenTransitionWin() throws Exception { assertSetThenGetBothModes("openTransitionWin", Boolean.TRUE, Boolean.TRUE); } @Test void setStringValue() throws Exception { assertSetThenGetBothModes("stringValue", "hello", "hello"); } } @Nested class SetterWithConversion { @Test void setIntFromDouble() throws Exception { assertSetThenGetBothModes("intValue", 6.5, 6); } @Test void setIntFromString() throws Exception { assertSetThenGetBothModes("intValue", "654", 654); } @Test void setStringFromInt() throws Exception { assertSetThenGetBothModes("stringValue", 25, "25"); } } @Nested class IndexAccess { @Test void listWithIndexVariable() throws Exception { assertBothModesMatch("list[index]"); } @Test void listWithObjectIndex() throws Exception { assertBothModesMatch("list[objectIndex]"); } @Test void arrayWithObjectIndex() throws Exception { assertBothModesMatch("array[objectIndex]"); } @Test void arrayWithMethodIndex() throws Exception { assertBothModesMatch("array[getObjectIndex()]"); } @Test void ternaryWithArrayLength() throws Exception { assertBothModes("(index == (array.length - 3)) ? 'toggle toggleSelected' : 'toggle'", "toggle toggleSelected"); } @Test void stringConcatWithIndex() throws Exception { assertBothModes("\"return toggleDisplay('excdisplay\" + index + \"', this)\"", "return toggleDisplay('excdisplay1', this)"); } @Test void mapSplitAccess() throws Exception { assertBothModes("map[mapKey].split('=')[0]", "StringStuff"); } @Test void nestedListAccess() throws Exception { assertBothModesMatch("booleanValues[index1][index2]"); } } @Nested class ArrayElements { @Test void charArrayAccess() throws Exception { assertBothModes("\"{Hello}\".toCharArray()[6]", '}'); } @Test void tapestryCharArray() throws Exception { assertBothModes("\"Tapestry\".toCharArray()[2]", 'p'); } @Test void listLiteral() throws Exception { assertBothModesMatch("{'1','2','3'}"); } @Test void booleanListLiteral() throws Exception { assertBothModesMatch("{ true, !false }"); } } @Nested class MethodCallsExtended { @Test void formatMethodWithProperty() throws Exception { assertBothModesMatch("getCurrentClass(\"Test\")"); } @Test void ternaryWithMethodResult() throws Exception { assertBothModes("disabled ? 'disabled' : 'othernot'", "disabled"); } @Test void printDeliveryConcat() throws Exception { assertBothModes("printDelivery ? 'javascript:toggle(' + bean2.id + ');' : ''", "javascript:toggle(1);"); } @Test void nestedMethodCall() throws Exception { assertBothModes("b.methodOfB(a.methodOfA(b)-1)", 0); } } @Nested class InterfaceInheritance { @Test void myMap() throws Exception { assertBothModesMatch("myMap"); } @Test void myMapDotTest() throws Exception { assertBothModesMatch("myMap.test"); } @Test void myMapArrayAccess() throws Exception { assertBothModesMatch("myMap.array[0]"); } @Test void myMapListAccess() throws Exception { assertBothModesMatch("myMap.list[1]"); } @Test void myMapFirstElement() throws Exception { assertBothModes("myMap[^]", 99); } @Test void myMapLastElement() throws Exception { assertBothModes("myMap[$]", null); } @Test void mapCompFormClientId() throws Exception { assertBothModes("map.comp.form.clientId", "form1"); } @Test void myTestTheMapKey() throws Exception { assertBothModes("myTest.theMap['key']", "value"); } } @Nested class DynamicSubscripts { @Test void mapFirstElement() throws Exception { assertBothModes("map[^]", 99); } @Test void mapLastElement() throws Exception { assertBothModes("map[$]", null); } @Test void listMidElement() throws Exception { assertBothModesMatch("getMap().list[|]"); } @Test void arrayLastElement() throws Exception { assertBothModesMatch("map.array[$]"); } } @Nested class ComplexExpressions { @Test void subExpressionWithThis() throws Exception { assertBothModesMatch("map.(#this)"); } @Test void subExpressionWithTernary() throws Exception { assertBothModesMatch("map.(#this != null ? #this['size'] : null)"); } @Test void firstElementSubExpression() throws Exception { assertBothModes("map[^].(#this == null ? 'empty' : #this)", 99); } @Test void lastElementSubExpression() throws Exception { assertBothModes("map[$].(#this == null ? 'empty' : #this)", "empty"); } @Test void lastElementWithRootRef() throws Exception { assertBothModesMatch("map[$].(#root == null ? 'empty' : #root)"); } @Test void arrayPlusMapSize() throws Exception { assertBothModesMatch("map.(array[2] + size())"); } @Test void nestedTernary() throws Exception { assertBothModes("sorted ? (readonly ? 'currentSortDesc' : 'currentSortAsc') : 'currentSortNone'", "currentSortAsc"); } @Test void selectedLocaleTernary() throws Exception { assertBothModes("((selected != null) && (currLocale.toString() == selected.toString())) ? 'first' : 'second'", "first"); } @Test void listLiteralWithProperties() throws Exception { assertBothModesMatch("{stringValue, getMap()}"); } @Test void getAssetWithTernary() throws Exception { assertBothModes("getAsset( (width?'Yes':'No')+'Icon' )", "NoIcon"); } } @Nested class PrimitiveNullHandling { @Test void setNullOnIntProperty() throws Exception { assertSetThenGetBothModes("intValue", null, 0); } @Test void setNullOnBooleanProperty() throws Exception { assertSetThenGetBothModes("booleanValue", null, false); } @Test void setValueOnIntProperty() throws Exception { assertSetThenGetBothModes("intValue", 42, 42); } @Test void setValueOnBooleanProperty() throws Exception { assertSetThenGetBothModes("booleanValue", true, true); } } @Nested class MethodCallsWithSimple { private Simple simpleRoot; private OgnlContext simpleContext; @BeforeEach void setUp() { simpleRoot = new Simple(); simpleContext = Ognl.createDefaultContext(simpleRoot, new DefaultMemberAccess(false)); } private void assertSimpleBothModes(String expression, Object expected) throws Exception { Object tree = Ognl.parseExpression(expression); Object interpreted = ((Node) tree).getValue(simpleContext.withRoot(simpleRoot), simpleRoot); assertEquals(expected, interpreted, "Interpreted failed for: " + expression); OgnlContext compiledCtx = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, simpleRoot, expression); Object compiledResult = compiled.getAccessor().get(compiledCtx, simpleRoot); assertEquals(expected, compiledResult, "Compiled failed for: " + expression); } private void assertSimpleBothModesMatch(String expression) throws Exception { Object tree = Ognl.parseExpression(expression); Object interpreted = ((Node) tree).getValue(simpleContext.withRoot(simpleRoot), simpleRoot); OgnlContext compiledCtx = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, simpleRoot, expression); Object compiledResult = compiled.getAccessor().get(compiledCtx, simpleRoot); assertEquals(interpreted, compiledResult, "Modes diverged for: " + expression + "\n interpreted: " + interpreted + "\n compiled: " + compiledResult); } @Test void hashCode_() throws Exception { assertSimpleBothModesMatch("hashCode()"); } @Test void booleanTernary() throws Exception { assertSimpleBothModes("getBooleanValue() ? \"here\" : \"\"", ""); } @Test void isDisabled() throws Exception { assertSimpleBothModes("isDisabled()", Boolean.TRUE); } @Test void isTruck() throws Exception { assertSimpleBothModes("isTruck", Boolean.TRUE); } @Test void isEditorDisabled() throws Exception { assertSimpleBothModes("isEditorDisabled()", Boolean.FALSE); } @Test void messagesFormat() throws Exception { assertSimpleBothModesMatch("messages.format('ShowAllCount', one)"); } @Test void varArgsNoArgs() throws Exception { assertSimpleBothModes("isThisVarArgsWorking()", Boolean.TRUE); } @Test void varArgsWithArgs() throws Exception { assertSimpleBothModes("isThisVarArgsWorking(three, rootValue)", Boolean.TRUE); } @Test void enumMethodArg() throws Exception { assertSimpleBothModes("getTestValue(@ognl.test.objects.SimpleEnum@ONE.value)", 2); } } @Nested class RootArrayAccess { private OgnlContext arrayContext; private String[] stringArray; private int[] intArray; @BeforeEach void setUp() { stringArray = new String[]{"hello", "world"}; intArray = new int[]{10, 20, 30}; } @Test void stringArrayLength() throws Exception { arrayContext = Ognl.createDefaultContext(stringArray, context.getMemberAccess()); Object tree = Ognl.parseExpression("length"); Object interpreted = ((Node) tree).getValue(arrayContext.withRoot(stringArray), stringArray); assertEquals(2, interpreted, "Interpreted failed"); OgnlContext compiledCtx = Ognl.createDefaultContext(stringArray, context.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, stringArray, "length"); Object compiledResult = compiled.getAccessor().get(compiledCtx, stringArray); assertEquals(2, compiledResult, "Compiled failed"); } @Test void stringArrayRootElement() throws Exception { arrayContext = Ognl.createDefaultContext(stringArray, context.getMemberAccess()); Object tree = Ognl.parseExpression("#root[1]"); Object interpreted = ((Node) tree).getValue(arrayContext.withRoot(stringArray), stringArray); assertEquals("world", interpreted, "Interpreted failed"); OgnlContext compiledCtx = Ognl.createDefaultContext(stringArray, context.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, stringArray, "#root[1]"); Object compiledResult = compiled.getAccessor().get(compiledCtx, stringArray); assertEquals("world", compiledResult, "Compiled failed"); } @Test void intArrayRootElement() throws Exception { arrayContext = Ognl.createDefaultContext(intArray, context.getMemberAccess()); Object tree = Ognl.parseExpression("#root[1]"); Object interpreted = ((Node) tree).getValue(arrayContext.withRoot(intArray), intArray); assertEquals(20, interpreted, "Interpreted failed"); OgnlContext compiledCtx = Ognl.createDefaultContext(intArray, context.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, intArray, "#root[1]"); Object compiledResult = compiled.getAccessor().get(compiledCtx, intArray); assertEquals(20, compiledResult, "Compiled failed"); } } @Nested class MethodCallsWithConversion { @Test void formatWithArray() throws Exception { assertBothModesMatch("format('key', array)"); } @Test void formatWithIntValue() throws Exception { assertBothModesMatch("format('key', intValue)"); } @Test void formatWithMapSize() throws Exception { assertBothModesMatch("format('key', map.size)"); } } @Nested class PrimitiveNullHandlingWithSimple { private Simple simpleRoot; private OgnlContext simpleContext; @BeforeEach void setUp() { simpleRoot = new Simple(); simpleRoot.setFloatValue(10.56f); simpleRoot.setIntValue(34); simpleContext = Ognl.createDefaultContext(simpleRoot, new DefaultMemberAccess(false)); } @Test void setNullOnFloatValue() throws Exception { // Interpreted: set null then get Object tree = Ognl.parseExpression("floatValue"); ((Node) tree).setValue(simpleContext.withRoot(simpleRoot), simpleRoot, null); Object interpreted = ((Node) Ognl.parseExpression("floatValue")).getValue( simpleContext.withRoot(simpleRoot), simpleRoot); assertEquals(0f, interpreted, "Interpreted set+get failed for: floatValue"); // Reset for compiled test simpleRoot.setFloatValue(10.56f); // Compiled: set null then get OgnlContext compiledCtx = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node setNode = Ognl.compileExpression(compiledCtx, simpleRoot, "floatValue"); setNode.getAccessor().set(compiledCtx, simpleRoot, null); OgnlContext compiledCtx2 = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node getNode = Ognl.compileExpression(compiledCtx2, simpleRoot, "floatValue"); Object compiled = getNode.getAccessor().get(compiledCtx2, simpleRoot); assertEquals(0f, compiled, "Compiled set+get failed for: floatValue"); } @Test void setNullOnIntValue() throws Exception { Object tree = Ognl.parseExpression("intValue"); ((Node) tree).setValue(simpleContext.withRoot(simpleRoot), simpleRoot, null); Object interpreted = ((Node) Ognl.parseExpression("intValue")).getValue( simpleContext.withRoot(simpleRoot), simpleRoot); assertEquals(0, interpreted, "Interpreted set+get failed for: intValue"); simpleRoot.setIntValue(34); OgnlContext compiledCtx = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node setNode = Ognl.compileExpression(compiledCtx, simpleRoot, "intValue"); setNode.getAccessor().set(compiledCtx, simpleRoot, null); OgnlContext compiledCtx2 = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node getNode = Ognl.compileExpression(compiledCtx2, simpleRoot, "intValue"); Object compiled = getNode.getAccessor().get(compiledCtx2, simpleRoot); assertEquals(0, compiled, "Compiled set+get failed for: intValue"); } @Test void setNullOnBooleanValue() throws Exception { Object tree = Ognl.parseExpression("booleanValue"); ((Node) tree).setValue(simpleContext.withRoot(simpleRoot), simpleRoot, true); ((Node) Ognl.parseExpression("booleanValue")).setValue( simpleContext.withRoot(simpleRoot), simpleRoot, null); Object interpreted = ((Node) Ognl.parseExpression("booleanValue")).getValue( simpleContext.withRoot(simpleRoot), simpleRoot); assertEquals(false, interpreted, "Interpreted set+get failed for: booleanValue"); simpleRoot.setBooleanValue(true); OgnlContext compiledCtx = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node setNode = Ognl.compileExpression(compiledCtx, simpleRoot, "booleanValue"); setNode.getAccessor().set(compiledCtx, simpleRoot, null); OgnlContext compiledCtx2 = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node getNode = Ognl.compileExpression(compiledCtx2, simpleRoot, "booleanValue"); Object compiled = getNode.getAccessor().get(compiledCtx2, simpleRoot); assertEquals(false, compiled, "Compiled set+get failed for: booleanValue"); } } /** * Tests requiring custom BeanProviderAccessor setup and DefaultMemberAccess(true), * matching InterfaceInheritanceTest's setUp(). */ @Nested class InterfaceInheritanceWithCustomSetup { private Root iRoot; private OgnlContext iContext; @BeforeEach void setUp() { iRoot = new Root(); iRoot.getBeans().setBean("testBean", new Bean1()); iRoot.getBeans().setBean("evenOdd", new EvenOdd()); List list = new ListSourceImpl(); list.add("test1"); iRoot.getMap().put("customList", list); iContext = Ognl.createDefaultContext(iRoot); OgnlRuntime.setPropertyAccessor(BeanProvider.class, new BeanProviderAccessor()); } private void assertIBothModes(String expression, Object expected) throws Exception { Object tree = Ognl.parseExpression(expression); Object interpreted = ((Node) tree).getValue(iContext.withRoot(iRoot), iRoot); assertEquals(expected, interpreted, "Interpreted failed for: " + expression); OgnlContext compiledCtx = Ognl.createDefaultContext(iRoot, iContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, iRoot, expression); Object compiledResult = compiled.getAccessor().get(compiledCtx, iRoot); assertEquals(expected, compiledResult, "Compiled failed for: " + expression); } private void assertIBothModesMatch(String expression) throws Exception { Object tree = Ognl.parseExpression(expression); Object interpreted = ((Node) tree).getValue(iContext.withRoot(iRoot), iRoot); OgnlContext compiledCtx = Ognl.createDefaultContext(iRoot, iContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, iRoot, expression); Object compiledResult = compiled.getAccessor().get(compiledCtx, iRoot); assertEquals(interpreted, compiledResult, "Modes diverged for: " + expression + "\n interpreted: " + interpreted + "\n compiled: " + compiledResult); } @Test void beansTestBean() throws Exception { assertIBothModesMatch("beans.testBean"); } @Disabled("Compiler evaluates expression during compilation for type inference, causing side effect on EvenOdd.getNext()") @Test void beansEvenOddNext() throws Exception { assertIBothModes("beans.evenOdd.next", "even"); } @Test void mapCustomListTotal() throws Exception { assertIBothModes("map.customList.total", 1); } @Test void mapCompGetCount() throws Exception { assertIBothModes("map.comp.getCount(genericIndex)", 0); } @Test void contentProviderHasChildren() throws Exception { assertIBothModes("contentProvider.hasChildren(property)", Boolean.TRUE); } @Test void myMapNull() throws Exception { assertIBothModes("myMap[null]", null); } @Test void myMapAssignNull() throws Exception { assertIBothModes("myMap[#x = null]", null); } @Test void myMapNullThenTest() throws Exception { assertIBothModesMatch("myMap.(null,test)"); } @Test void myMapBracketAccess() throws Exception { assertIBothModesMatch("[\"myMap\"]"); } @Test void myMapNullSetValue() throws Exception { assertIBothModes("myMap[null] = 25", 25); } } /** * Tests with Indexed root object, matching IndexedPropertyTest's setUp(). */ @Nested class IndexedProperties { private Indexed indexed; private OgnlContext iContext; @BeforeEach void setUp() { indexed = new Indexed(); iContext = Ognl.createDefaultContext(indexed); } private void assertIndexedBothModes(String expression, Object expected) throws Exception { Object tree = Ognl.parseExpression(expression); Object interpreted = ((Node) tree).getValue(iContext.withRoot(indexed), indexed); assertEquals(expected, interpreted, "Interpreted failed for: " + expression); OgnlContext compiledCtx = Ognl.createDefaultContext(indexed, iContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, indexed, expression); Object compiledResult = compiled.getAccessor().get(compiledCtx, indexed); assertEquals(expected, compiledResult, "Compiled failed for: " + expression); } private void assertIndexedBothModesMatch(String expression) throws Exception { Object tree = Ognl.parseExpression(expression); Object interpreted = ((Node) tree).getValue(iContext.withRoot(indexed), indexed); OgnlContext compiledCtx = Ognl.createDefaultContext(indexed, iContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, indexed, expression); Object compiledResult = compiled.getAccessor().get(compiledCtx, indexed); assertEquals(interpreted, compiledResult, "Modes diverged for: " + expression + "\n interpreted: " + interpreted + "\n compiled: " + compiledResult); } @Test void getValues() throws Exception { assertIndexedBothModesMatch("getValues"); } @Test void bracketValues() throws Exception { assertIndexedBothModesMatch("[\"values\"]"); } @Test void getValuesIndex0() throws Exception { assertIndexedBothModesMatch("getValues()[0]"); } @Test void valuesIndex0() throws Exception { assertIndexedBothModesMatch("values[0]"); } @Test void valuesFirst() throws Exception { assertIndexedBothModes("values[^]", indexed.getValues(0)); } @Test void valuesMid() throws Exception { assertIndexedBothModes("values[|]", indexed.getValues(1)); } @Test void valuesLast() throws Exception { assertIndexedBothModes("values[$]", indexed.getValues(2)); } @Test void getTitleWithListSize() throws Exception { assertIndexedBothModes("getTitle(list.size)", "Title count 3"); } @Test void sourceTotal() throws Exception { assertIndexedBothModes("source.total", 1); } @Test void listLongValue() throws Exception { assertIndexedBothModes("list[2].longValue()", 3L); } } /** * Additional IndexAccess tests not covered by the main IndexAccess nested class. */ @Nested class IndexAccessExtended { @Test void arrayGenericIndex() throws Exception { assertBothModesMatch("array[genericIndex]"); } @Test void booleanArraySelfObjectIndex() throws Exception { assertBothModes("booleanArray[self.objectIndex]", Boolean.FALSE); } @Test void booleanArrayGetObjectIndex() throws Exception { assertBothModes("booleanArray[getObjectIndex()]", Boolean.FALSE); } @Test void tabSearchCriteriaDisplayName() throws Exception { assertBothModes("tab.searchCriteria[index1].displayName", "Woodland creatures"); } @Test void tabSearchCriteriaSelections() throws Exception { assertBothModes("tab.searchCriteriaSelections[index1][index2]", Boolean.TRUE); } @Test void mapBarValueSetGet() throws Exception { assertSetThenGetBothModes("map['bar'].value", 50, 50); } @Test void mapValueId() throws Exception { assertBothModes("map.value.id", 1L); } @Test void indexerLineIndex() throws Exception { assertBothModes("indexer.line[index]", "line:1"); } } /** * Additional setter with conversion tests. */ @Nested class SetterWithConversionExtended { @Test void setStringFromFloat() throws Exception { assertSetThenGetBothModes("stringValue", 100.25f, "100.25"); } @Test void setAnotherStringFromInt() throws Exception { assertSetThenGetBothModes("anotherStringValue", 0, "0"); } @Test void setAnotherStringFromDouble() throws Exception { assertSetThenGetBothModes("anotherStringValue", 0.5, "0.5"); } @Test void setAnotherIntFromString() throws Exception { assertSetThenGetBothModes("anotherIntValue", "5", 5); } @Test void setAnotherIntFromDouble() throws Exception { assertSetThenGetBothModes("anotherIntValue", 100.25, 100); } @Test void setIntFromLargeDouble() throws Exception { assertSetThenGetBothModes("intValue", 1025.87645, 1025); } } /** * Additional setter path tests. */ @Nested class SetterPathsExtended { @Test void setSettableListSpecialIndex() throws Exception { assertSetThenGetBothModes("settableList[$]", "quux", "quux"); } @Test void setMapNewValue() throws Exception { assertSetThenGetBothModes("map.newValue", 555, 555); } } /** * Additional Simple-root method call tests. */ @Nested class MethodCallsWithSimpleExtended { private Simple simpleRoot; private OgnlContext simpleContext; @BeforeEach void setUp() { simpleRoot = new Simple(); simpleContext = Ognl.createDefaultContext(simpleRoot, new DefaultMemberAccess(false)); } private void assertSimpleBothModesMatch(String expression) throws Exception { Object tree = Ognl.parseExpression(expression); Object interpreted = ((Node) tree).getValue(simpleContext.withRoot(simpleRoot), simpleRoot); OgnlContext compiledCtx = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, simpleRoot, expression); Object compiledResult = compiled.getAccessor().get(compiledCtx, simpleRoot); assertEquals(interpreted, compiledResult, "Modes diverged for: " + expression + "\n interpreted: " + interpreted + "\n compiled: " + compiledResult); } private void assertSimpleBothModes(String expression, Object expected) throws Exception { Object tree = Ognl.parseExpression(expression); Object interpreted = ((Node) tree).getValue(simpleContext.withRoot(simpleRoot), simpleRoot); assertEquals(expected, interpreted, "Interpreted failed for: " + expression); OgnlContext compiledCtx = Ognl.createDefaultContext(simpleRoot, simpleContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, simpleRoot, expression); Object compiledResult = compiled.getAccessor().get(compiledCtx, simpleRoot); assertEquals(expected, compiledResult, "Compiled failed for: " + expression); } @Test void messagesFormatArrayArg() throws Exception { assertSimpleBothModesMatch("messages.format('ShowAllCount', {one})"); } @Test void messagesFormatTwoArrayArgs() throws Exception { assertSimpleBothModesMatch("messages.format('ShowAllCount', {one, two})"); } @Test void messagesFormatTwoArgs() throws Exception { assertSimpleBothModesMatch("messages.format('ShowAllCount', one, two)"); } @Test void getValueIsTrue() throws Exception { assertSimpleBothModes("getValueIsTrue(!false) ? \"\" : \"here\"", ""); } @Test void getDisplayValue() throws Exception { assertSimpleBothModes("getDisplayValue(methodsTest.allowDisplay)", "test"); } @Test void testMethodsGetBean() throws Exception { assertSimpleBothModesMatch("testMethods.getBean('TestBean')"); } @Test void testMethodsTestProperty() throws Exception { assertSimpleBothModesMatch("testMethods.testProperty"); } @Test void testMethodsArgsTest1() throws Exception { assertSimpleBothModesMatch("testMethods.argsTest1({one})"); } @Test void testMethodsArgsTest2() throws Exception { assertSimpleBothModesMatch("testMethods.argsTest2({one})"); } @Test void testMethodsArgsTest3() throws Exception { assertSimpleBothModes("testMethods.argsTest3({one})", "List: [1]"); } @Test void testMethodsShowListObjectList() throws Exception { assertSimpleBothModesMatch("testMethods.showList(testMethods.getObjectList())"); } @Test void testMethodsShowListStringList() throws Exception { assertSimpleBothModesMatch("testMethods.showList(testMethods.getStringList())"); } @Test void testMethodsShowListStringArray() throws Exception { assertSimpleBothModesMatch("testMethods.showList(testMethods.getStringArray())"); } @Test void testMethodsAvg() throws Exception { assertSimpleBothModesMatch("testMethods.avg({ 5, 5 })"); } } /** * Additional PropertyTest expressions. */ @Nested class PropertyTestExpressions { @Test void testStringNotNull() throws Exception { assertBothModes("testString != null && !false", Boolean.TRUE); } @Test void negatedRenderNavAndReadonly() throws Exception { assertBothModes("!getRenderNavigation() and !getReadonly()", Boolean.TRUE); } @Test void mapSizeFromStaticField() throws Exception { assertBothModesMatch("map[@ognl.test.objects.Root@SIZE_STRING]"); } @Test void mapSizeStringAccess() throws Exception { assertBothModesMatch("map[\"size\"]"); } @Test void mapConcatSizeAccess() throws Exception { assertBothModesMatch("map[(\"s\" + \"i\") + \"ze\"]"); } @Test void mapList() throws Exception { assertBothModesMatch("map.list"); } @Test void mapArrayFirst() throws Exception { assertBothModesMatch("map.array[0]"); } @Test void mapListSecond() throws Exception { assertBothModesMatch("map.list[1]"); } @Test void mapArrayLast() throws Exception { assertBothModesMatch("map.array[$]"); } @Test void mapBracketAccess() throws Exception { assertBothModesMatch("[\"map\"]"); } @Test void flyingMonkey() throws Exception { assertBothModes("flyingMonkey", Boolean.TRUE); } @Test void openTransitionWin() throws Exception { assertBothModes("openTransitionWin", Boolean.FALSE); } @Test void disableButtonConcat() throws Exception { assertBothModes("'disableButton(this,\"' + map.get('button-testing') + '\");clearElement("testFtpMessage")'", "disableButton(this,\"null\");clearElement("testFtpMessage")"); } @Test void propertyBean3Value() throws Exception { assertBothModes("property.bean3.value != null", Boolean.TRUE); } @Test void propertyBean3ValueConcat() throws Exception { assertBothModes("property.bean3.value + '(this.checked)'", "100(this.checked)"); } @Test void getPropertyGetBean3() throws Exception { assertBothModesMatch("getProperty().getBean3()"); } @Test void getIndexedPropertyViaMap() throws Exception { assertBothModesMatch("getIndexedProperty(property.bean3.map[\"bar\"])"); } @Test void stringLengthNotNull() throws Exception { assertBothModes("stringValue != null && stringValue.length() > 0", Boolean.FALSE); } } /** * Tests with GameGeneric root for generics support. */ @Nested class Generics { private BaseGeneric generic; private OgnlContext gContext; @BeforeEach void setUp() { generic = new GameGeneric(); gContext = Ognl.createDefaultContext(generic); } @Test void genericServiceGetFullMessageFor() throws Exception { Object tree = Ognl.parseExpression("service.getFullMessageFor(value, null)"); Object interpreted = ((Node) tree).getValue(gContext.withRoot(generic), generic); assertEquals("Halo 3", interpreted, "Interpreted failed"); OgnlContext compiledCtx = Ognl.createDefaultContext(generic, gContext.getMemberAccess()); Node compiled = Ognl.compileExpression(compiledCtx, generic, "service.getFullMessageFor(value, null)"); Object compiledResult = compiled.getAccessor().get(compiledCtx, generic); assertEquals("Halo 3", compiledResult, "Compiled failed"); } @Test void genericIdsSetGet() throws Exception { Long[] expected = new Long[]{1L, 101L}; // Interpreted Ognl.setValue("ids", gContext, generic, expected); Object interpreted = Ognl.getValue("ids", gContext, generic); assertEquals(Arrays.toString(expected), Arrays.toString((Long[]) interpreted), "Interpreted failed"); // Compiled OgnlContext compiledCtx = Ognl.createDefaultContext(generic, gContext.getMemberAccess()); Node setNode = Ognl.compileExpression(compiledCtx, generic, "ids"); setNode.getAccessor().set(compiledCtx, generic, expected); OgnlContext compiledCtx2 = Ognl.createDefaultContext(generic, gContext.getMemberAccess()); Node getNode = Ognl.compileExpression(compiledCtx2, generic, "ids"); Object compiledResult = getNode.getAccessor().get(compiledCtx2, generic); assertEquals(Arrays.toString(expected), Arrays.toString((Long[]) compiledResult), "Compiled failed"); } } /** * Tests with IndexedSetObject root. */ @Nested class IndexedSetObjectTests { private IndexedSetObject indexedSet; private OgnlContext iContext; @BeforeEach void setUp() { indexedSet = new IndexedSetObject(); iContext = Ognl.createDefaultContext(indexedSet); } @Test void thingXValSetGet() throws Exception { // Interpreted Ognl.setValue("thing[\"x\"].val", iContext, indexedSet, 2); Object interpreted = Ognl.getValue("thing[\"x\"].val", iContext, indexedSet); assertEquals(2, interpreted, "Interpreted failed"); // Reset indexedSet = new IndexedSetObject(); // Compiled OgnlContext compiledCtx = Ognl.createDefaultContext(indexedSet, iContext.getMemberAccess()); Node setNode = Ognl.compileExpression(compiledCtx, indexedSet, "thing[\"x\"].val"); setNode.getAccessor().set(compiledCtx, indexedSet, 2); OgnlContext compiledCtx2 = Ognl.createDefaultContext(indexedSet, iContext.getMemberAccess()); Node getNode = Ognl.compileExpression(compiledCtx2, indexedSet, "thing[\"x\"].val"); Object compiledResult = getNode.getAccessor().get(compiledCtx2, indexedSet); assertEquals(2, compiledResult, "Compiled failed"); } } /** * Array element set operations with type conversion. */ @Nested class ArrayElementSetOperations { @Test void intArraySetWithInt() throws Exception { int[] arr = new int[]{10, 20}; OgnlContext ctx = Ognl.createDefaultContext(arr, context.getMemberAccess()); // Interpreted Ognl.setValue("#root[1]", ctx, arr, 50); assertEquals(50, Ognl.getValue("#root[1]", ctx, arr), "Interpreted failed"); // Reset arr[1] = 20; // Compiled OgnlContext compiledCtx = Ognl.createDefaultContext(arr, context.getMemberAccess()); Node setNode = Ognl.compileExpression(compiledCtx, arr, "#root[1]"); setNode.getAccessor().set(compiledCtx, arr, 50); OgnlContext compiledCtx2 = Ognl.createDefaultContext(arr, context.getMemberAccess()); Node getNode = Ognl.compileExpression(compiledCtx2, arr, "#root[1]"); Object compiled = getNode.getAccessor().get(compiledCtx2, arr); assertEquals(50, compiled, "Compiled failed"); } @Test void intArraySetWithString() throws Exception { int[] arr = new int[]{10, 20}; OgnlContext ctx = Ognl.createDefaultContext(arr, context.getMemberAccess()); // Interpreted Ognl.setValue("#root[1]", ctx, arr, "50"); assertEquals(50, Ognl.getValue("#root[1]", ctx, arr), "Interpreted failed"); // Reset arr[1] = 20; // Compiled OgnlContext compiledCtx = Ognl.createDefaultContext(arr, context.getMemberAccess()); Node setNode = Ognl.compileExpression(compiledCtx, arr, "#root[1]"); setNode.getAccessor().set(compiledCtx, arr, "50"); OgnlContext compiledCtx2 = Ognl.createDefaultContext(arr, context.getMemberAccess()); Node getNode = Ognl.compileExpression(compiledCtx2, arr, "#root[1]"); Object compiled = getNode.getAccessor().get(compiledCtx2, arr); assertEquals(50, compiled, "Compiled failed"); } @Test void rootIntValueSetWithString() throws Exception { assertSetThenGetBothModes("intValue", "50", 50); } } } ================================================ FILE: ognl/src/test/java/ognl/test/GenericsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.BaseGeneric; import ognl.test.objects.GameGeneric; import ognl.test.objects.GameGenericObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; class GenericsTest { private BaseGeneric generic; private OgnlContext context; @BeforeEach void setUp() { generic = new GameGeneric(); context = Ognl.createDefaultContext(generic); } @Test void testIds() throws Exception { Long[] expected = new Long[]{1L, 101L}; Ognl.setValue("ids", context, generic, expected); Long[] actual = (Long[]) Ognl.getValue("ids", context, generic); assertArrayEquals(expected, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/InExpressionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Test for OGNL-118. */ class InExpressionTest { @Test void test_String_In() throws Exception { OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); Object node = Ognl.parseExpression("#name in {\"Greenland\", \"Austin\", \"Africa\", \"Rome\"}"); Object root = null; context.put("name", "Austin"); assertEquals(Boolean.TRUE, Ognl.getValue(node, context, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/IndexAccessTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.MethodFailedException; import ognl.NoSuchPropertyException; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.IndexedSetObject; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class IndexAccessTest { private Root root; private IndexedSetObject indexedSet; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); indexedSet = new IndexedSetObject(); context = Ognl.createDefaultContext(root); } @Test void testListIndex() throws Exception { Object actual = Ognl.getValue("list[index]", context, root); assertEquals(root.getList().get(root.getIndex()), actual); } @Test void testListObjectIndex() throws Exception { Object actual = Ognl.getValue("list[objectIndex]", context, root); assertEquals(root.getList().get(root.getObjectIndex()), actual); } @Test void testArrayObjectIndex() throws Exception { Object actual = Ognl.getValue("array[objectIndex]", context, root); assertEquals(root.getArray()[root.getObjectIndex()], actual); } @Test void testArrayGetObjectIndex() throws Exception { Object actual = Ognl.getValue("array[getObjectIndex()]", context, root); assertEquals(root.getArray()[root.getObjectIndex()], actual); } @Test void testArrayGenericIndex() throws Exception { Object actual = Ognl.getValue("array[genericIndex]", context, root); assertEquals(root.getArray()[(Integer) root.getGenericIndex()], actual); } @Test void testBooleanArraySelfObjectIndex() throws Exception { Object actual = Ognl.getValue("booleanArray[self.objectIndex]", context, root); assertEquals(Boolean.FALSE, actual); } @Test void testBooleanArrayGetObjectIndex() throws Exception { Object actual = Ognl.getValue("booleanArray[getObjectIndex()]", context, root); assertEquals(Boolean.FALSE, actual); } @Test void testBooleanArrayNullIndex() { assertThrows(NoSuchPropertyException.class, () -> Ognl.getValue("booleanArray[nullIndex]", context, root), "nullIndex"); } @Test void testListSizeMinusOne() { assertThrows(MethodFailedException.class, () -> Ognl.getValue("list[size() - 1]", context, root), "size()"); } @Test void testToggleToggleSelected() throws Exception { Object actual = Ognl.getValue("(index == (array.length - 3)) ? 'toggle toggleSelected' : 'toggle'", context, root); assertEquals("toggle toggleSelected", actual); } @Test void testToggleDisplay() throws Exception { Object actual = Ognl.getValue("\"return toggleDisplay('excdisplay\"+index+\"', this)\"", context, root); assertEquals("return toggleDisplay('excdisplay1', this)", actual); } @Test void testMapMapKeySplit() throws Exception { Object actual = Ognl.getValue("map[mapKey].split('=')[0]", context, root); assertEquals("StringStuff", actual); } @Test void testBooleanValuesIndex1Index2() throws Exception { Object actual = Ognl.getValue("booleanValues[index1][index2]", context, root); assertEquals(Boolean.FALSE, actual); } @Test void testTabSearchCriteriaDisplayName() throws Exception { Object actual = Ognl.getValue("tab.searchCriteria[index1].displayName", context, root); assertEquals("Woodland creatures", actual); } @Test void testTabSearchCriteriaSelections() throws Exception { Object actual = Ognl.getValue("tab.searchCriteriaSelections[index1][index2]", context, root); assertEquals(Boolean.TRUE, actual); } @Test void testTabSearchCriteriaSelectionsSetValue() throws Exception { Ognl.setValue("tab.searchCriteriaSelections[index1][index2]", context, root, Boolean.FALSE); Object actual = Ognl.getValue("tab.searchCriteriaSelections[index1][index2]", context, root); assertEquals(Boolean.FALSE, actual); } @Test void testMapBarValue() throws Exception { Ognl.setValue("map['bar'].value", context, root, 50); Object actual = Ognl.getValue("map['bar'].value", context, root); assertEquals(50, actual); } @Test void testIndexedSetThingXVal() throws Exception { Ognl.setValue("thing[\"x\"].val", context, indexedSet, 2); Object actual = Ognl.getValue("thing[\"x\"].val", context, indexedSet); assertEquals(2, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/IndexedPropertyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Indexed; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class IndexedPropertyTest { private Indexed indexed; private Root root; private OgnlContext context; @BeforeEach void setUp() { indexed = new Indexed(); root = new Root(); context = Ognl.createDefaultContext(root); } @Test void testGetValues() throws Exception { Object actual = Ognl.getValue("getValues", context, indexed); assertEquals(indexed.getValues(), actual); } @Test void testValues() throws Exception { Object actual = Ognl.getValue("[\"values\"]", context, indexed); assertEquals(indexed.getValues(), actual); } @Test void testValuesIndex0() throws Exception { Object actual = Ognl.getValue("[0]", context, indexed.getValues()); assertEquals(indexed.getValues()[0], actual); } @Test void testGetValuesIndex0() throws Exception { Object actual = Ognl.getValue("getValues()[0]", context, indexed); assertEquals(indexed.getValues()[0], actual); } @Test void testValuesIndex0Direct() throws Exception { Object actual = Ognl.getValue("values[0]", context, indexed); assertEquals(indexed.getValues(0), actual); } @Test void testValuesCaret() throws Exception { Object actual = Ognl.getValue("values[^]", context, indexed); assertEquals(indexed.getValues(0), actual); } @Test void testValuesPipe() throws Exception { Object actual = Ognl.getValue("values[|]", context, indexed); assertEquals(indexed.getValues(1), actual); } @Test void testValuesDollar() throws Exception { Object actual = Ognl.getValue("values[$]", context, indexed); assertEquals(indexed.getValues(2), actual); } @Test void testSetValuesIndex1() throws Exception { Ognl.setValue("values[1]", context, indexed, "xxxx" + "xxx"); Object actual = Ognl.getValue("values[1]", context, indexed); assertEquals("xxxxxxx", actual); } @Test void testSetValuesIndex2() throws Exception { Ognl.getValue("setValues(2, \"xxxx\")", context, indexed); Object actual = Ognl.getValue("values[2]", context, indexed); assertEquals("xxxx", actual); } @Test void testGetTitle() throws Exception { Object actual = Ognl.getValue("getTitle(list.size)", context, indexed); assertEquals("Title count 3", actual); } @Test void testSourceTotal() throws Exception { Object actual = Ognl.getValue("source.total", context, indexed); assertEquals(1, actual); } @Test void testIndexerLine() throws Exception { Object actual = Ognl.getValue("indexer.line[index]", context, root); assertEquals("line:1", actual); } @Test void testListLongValue() throws Exception { Object actual = Ognl.getValue("list[2].longValue()", context, indexed); assertEquals(3L, actual); } @Test void testMapValueId() throws Exception { Object actual = Ognl.getValue("map.value.id", context, root); assertEquals(1L, actual); } @Test void testPropertyHoodak() throws Exception { Ognl.setValue("property['hoodak']", context, indexed, "random string"); Object actual = Ognl.getValue("property['hoodak']", context, indexed); assertEquals("random string", actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/InheritedMethodsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Node; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.BaseBean; import ognl.test.objects.FirstBean; import ognl.test.objects.Root; import ognl.test.objects.SecondBean; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests functionality of casting inherited method expressions. */ class InheritedMethodsTest { private static final Root ROOT = new Root(); @Test void test_Base_Inheritance() throws Exception { OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); String expression = "map.bean.name"; BaseBean first = new FirstBean(); BaseBean second = new SecondBean(); ROOT.getMap().put("bean", first); Node node = Ognl.compileExpression(context, ROOT, expression); assertEquals(first.getName(), node.getAccessor().get(context, ROOT)); ROOT.getMap().put("bean", second); assertEquals(second.getName(), node.getAccessor().get(context, ROOT)); } } ================================================ FILE: ognl/src/test/java/ognl/test/InterfaceInheritanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlRuntime; import ognl.test.objects.Bean1; import ognl.test.objects.BeanProvider; import ognl.test.objects.BeanProviderAccessor; import ognl.test.objects.EvenOdd; import ognl.test.objects.ListSourceImpl; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class InterfaceInheritanceTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); root.getBeans().setBean("testBean", new Bean1()); root.getBeans().setBean("evenOdd", new EvenOdd()); List list = new ListSourceImpl(); list.add("test1"); root.getMap().put("customList", list); context = Ognl.createDefaultContext(root); OgnlRuntime.setPropertyAccessor(BeanProvider.class, new BeanProviderAccessor()); } @Test void testMyMap() throws Exception { Object actual = Ognl.getValue("myMap", context, root); assertEquals(root.getMyMap(), actual); } @Test void testMyMapTest() throws Exception { Object actual = Ognl.getValue("myMap.test", context, root); assertEquals(root, actual); } @Test void testMyMapList() throws Exception { Object actual = Ognl.getValue("list", context, root.getMyMap()); assertEquals(root.getList(), actual); } @Test void testMyMapArray0() throws Exception { Object actual = Ognl.getValue("myMap.array[0]", context, root); assertEquals(root.getArray()[0], actual); } @Test void testMyMapList1() throws Exception { Object actual = Ognl.getValue("myMap.list[1]", context, root); assertEquals(root.getList().get(1), actual); } @Test void testMyMapCaret() throws Exception { Object actual = Ognl.getValue("myMap[^]", context, root); assertEquals(99, actual); } @Test void testMyMapDollar() throws Exception { Object actual = Ognl.getValue("myMap[$]", context, root); assertNull(actual); } @Test void testMyMapArrayDollar() throws Exception { Object actual = Ognl.getValue("array[$]", context, root.getMyMap()); assertEquals(root.getArray()[root.getArray().length - 1], actual); } @Test void testMyMapString() throws Exception { Object actual = Ognl.getValue("[\"myMap\"]", context, root); assertEquals(root.getMyMap(), actual); } @Test void testMyMapNull() throws Exception { Object actual = Ognl.getValue("myMap[null]", context, root); assertNull(actual); } @Test void testMyMapXNull() throws Exception { Object actual = Ognl.getValue("myMap[#x = null]", context, root); assertNull(actual); } @Test void testMyMapNullTest() throws Exception { Object actual = Ognl.getValue("myMap.(null,test)", context, root); assertEquals(root, actual); } @Test void testMyMapNull25() throws Exception { Object actual = Ognl.getValue("myMap[null] = 25", context, root); assertEquals(25, actual); } @Test void testMyMapNull50() throws Exception { Ognl.setValue("myMap[null]", context, root, 50); Object actual = Ognl.getValue("myMap[null]", context, root); assertEquals(50, actual); } @Test void testBeansTestBean() throws Exception { Object actual = Ognl.getValue("beans.testBean", context, root); assertEquals(root.getBeans().getBean("testBean"), actual); } @Test void testBeansEvenOddNext() throws Exception { Object actual = Ognl.getValue("beans.evenOdd.next", context, root); assertEquals("even", actual); } @Test void testMapCompFormClientId() throws Exception { Object actual = Ognl.getValue("map.comp.form.clientId", context, root); assertEquals("form1", actual); } @Test void testMapCompGetCount() throws Exception { Object actual = Ognl.getValue("map.comp.getCount(genericIndex)", context, root); assertEquals(0, actual); } @Test void testMapCustomListTotal() throws Exception { Object actual = Ognl.getValue("map.customList.total", context, root); assertEquals(1, actual); } @Test void testMyTestTheMapKey() throws Exception { Object actual = Ognl.getValue("myTest.theMap['key']", context, root); assertEquals("value", actual); } @Test void testContentProviderHasChildren() throws Exception { Object actual = Ognl.getValue("contentProvider.hasChildren(property)", context, root); assertEquals(Boolean.TRUE, actual); } @Test void testObjectIndexInstanceOf() throws Exception { Object actual = Ognl.getValue("objectIndex instanceof java.lang.Object", context, root); assertEquals(Boolean.TRUE, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/IsTruckTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class IsTruckTest { @Test void testIsTruckMethod() throws Exception { OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); boolean actual = (Boolean) Ognl.getValue("isTruck", context, new TruckHolder()); assertTrue(actual); } } class TruckHolder { public boolean getIsTruck() { return true; } } ================================================ FILE: ognl/src/test/java/ognl/test/Issue286Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test for Issue #286: OGNL choosing method on unexported class rather than exported interface *

* The issue occurs when OGNL selects a method from an internal implementation class * (like sun.security.x509.X509CertImpl) instead of the public interface * (java.security.cert.X509Certificate), causing IllegalAccessException due to module restrictions. */ class Issue286Test { @Test void x509CertificateMethodResolution() { // Simulates the issue where OGNL selects a method from an internal implementation // (like sun.security.x509.X509CertImpl for X509Certificate) instead of the public interface TestInterface obj = new InternalImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); // This should select the method from TestInterface, not InternalImplementation assertDoesNotThrow(() -> { Object result = Ognl.getValue("publicMethod()", context, obj); assertNotNull(result); }, "OGNL should select the method from the public interface, not the internal implementation"); } /** * Test that demonstrates the preference for interface methods over implementation methods * when both have the same signature. */ @Test void interfaceMethodPreferredOverImplementation() throws Exception { TestInterface obj = new InternalImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); // This expression should work because OGNL should prefer the interface method Object result = Ognl.getValue("publicMethod()", context, obj); assertNotNull(result); } /** * Test with a more complex example involving collections */ @Test void interfaceMethodOnArrayElement() throws Exception { TestInterface[] array = new TestInterface[]{new InternalImplementation()}; OgnlContext context = Ognl.createDefaultContext(array); // This simulates the original issue: calling a method on an array element Object result = Ognl.getValue("[0].publicMethod()", context, array); assertNotNull(result); } /** * Test method resolution with parameters to ensure full method matching logic is exercised */ @Test void interfaceMethodWithParameters() throws Exception { ParameterizedInterface obj = new ParameterizedImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result = Ognl.getValue("process('test')", context, obj); assertNotNull(result); } /** * Test with multiple interfaces implementing the same method */ @Test void multipleInterfacesWithSameMethod() throws Exception { MultiInterfaceImplementation obj = new MultiInterfaceImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); // Should prefer interface methods regardless of which interface Object result = Ognl.getValue("getValue()", context, obj); assertNotNull(result); } /** * Test public class vs package-private class preference */ @Test void publicClassPreferredOverPackagePrivate() throws Exception { BaseInterface obj = new PublicImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result = Ognl.getValue("baseMethod()", context, obj); assertNotNull(result); } /** * Test with nested method calls */ @Test void nestedMethodCallsOnInterface() throws Exception { ContainerInterface container = new ContainerImplementation(); OgnlContext context = Ognl.createDefaultContext(container); Object result = Ognl.getValue("getChild().publicMethod()", context, container); assertNotNull(result); } /** * Test overloaded methods */ @Test void overloadedInterfaceMethods() throws Exception { OverloadedInterface obj = new OverloadedImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result1 = Ognl.getValue("compute(5)", context, obj); assertNotNull(result1); assertEquals(10, result1); Object result2 = Ognl.getValue("compute(5, 10)", context, obj); assertNotNull(result2); assertEquals(15, result2); } /** * Test with abstract class in hierarchy */ @Test void abstractClassInHierarchy() throws Exception { AbstractInterface obj = new ConcreteImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result = Ognl.getValue("abstractMethod()", context, obj); assertNotNull(result); assertEquals("concrete", result); } /** * Test with generics */ @Test void genericInterfaceMethod() throws Exception { GenericInterface obj = new GenericImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result = Ognl.getValue("transform('input')", context, obj); assertNotNull(result); assertEquals("TRANSFORMED: input", result); } /** * Test with collection return types */ @Test void methodReturningCollection() throws Exception { CollectionInterface obj = new CollectionImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result = Ognl.getValue("getItems().size()", context, obj); assertNotNull(result); assertEquals(3, result); } /** * Test with inheritance hierarchy - interface extends interface */ @Test void extendedInterfaceMethod() throws Exception { ExtendedInterface obj = new ExtendedImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result1 = Ognl.getValue("baseMethod()", context, obj); assertNotNull(result1); Object result2 = Ognl.getValue("extendedMethod()", context, obj); assertNotNull(result2); } /** * Test method that returns an interface type */ @Test void methodReturningInterface() throws Exception { FactoryInterface factory = new FactoryImplementation(); OgnlContext context = Ognl.createDefaultContext(factory); Object result = Ognl.getValue("create().publicMethod()", context, factory); assertNotNull(result); assertEquals("result", result); } /** * Test with null parameters */ @Test void methodWithNullParameter() throws Exception { NullableInterface obj = new NullableImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result = Ognl.getValue("handleNull(null)", context, obj); assertNotNull(result); assertEquals("null handled", result); } /** * Test with primitive parameters and autoboxing */ @Test void methodWithPrimitiveParameters() throws Exception { PrimitiveInterface obj = new PrimitiveImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result = Ognl.getValue("add(3, 7)", context, obj); assertNotNull(result); assertEquals(10, result); } /** * Test with actual JDK classes - HashMap (concrete) vs Map (interface) * This tests that OGNL prefers interface methods when available */ @Test void jdkInterfacePreferredOverConcreteClass() throws Exception { Map map = new HashMap<>(); map.put("key", "value"); OgnlContext context = Ognl.createDefaultContext(map); // Call methods that exist on both Map interface and HashMap class Object size = Ognl.getValue("size()", context, map); assertEquals(1, size); Object isEmpty = Ognl.getValue("isEmpty()", context, map); assertEquals(false, isEmpty); Object value = Ognl.getValue("get('key')", context, map); assertEquals("value", value); } /** * Test method resolution choosing between multiple method sources */ @Test void methodResolutionWithInheritance() throws Exception { // Use a list to test interface vs implementation preference List list = List.of("a", "b", "c"); OgnlContext context = Ognl.createDefaultContext(list); Object result = Ognl.getValue("size()", context, list); assertEquals(3, result); Object first = Ognl.getValue("get(0)", context, list); assertEquals("a", first); } /** * Test that verifies method resolution works correctly with package-private implementation */ @Test void packagePrivateImplementation() throws Exception { PackagePrivateImpl obj = new PackagePrivateImpl(); OgnlContext context = Ognl.createDefaultContext(obj); Object result = Ognl.getValue("publicMethod()", context, obj); assertNotNull(result); assertEquals("package-private", result); } /** * Test with method calls on objects that implement multiple interfaces */ @Test void multipleInterfaceInheritance() throws Exception { CombinedInterface obj = new CombinedImplementation(); OgnlContext context = Ognl.createDefaultContext(obj); Object result1 = Ognl.getValue("methodA()", context, obj); assertEquals("A", result1); Object result2 = Ognl.getValue("methodB()", context, obj); assertEquals("B", result2); } /** * Test method resolution with classes from different packages * This indirectly tests the isLikelyAccessible() logic */ @Test void methodResolutionAcrossPackages() throws Exception { // Test that common JDK classes work correctly String str = "test"; OgnlContext context = Ognl.createDefaultContext(str); Object result = Ognl.getValue("length()", context, str); assertEquals(4, result); Object upper = Ognl.getValue("toUpperCase()", context, str); assertEquals("TEST", upper); } /** * Test with java.util classes to ensure proper method resolution */ @Test void javaUtilClassMethodResolution() throws Exception { java.util.ArrayList list = new java.util.ArrayList<>(); list.add("item1"); list.add("item2"); OgnlContext context = Ognl.createDefaultContext(list); Object size = Ognl.getValue("size()", context, list); assertEquals(2, size); // Test method that exists on List interface Object first = Ognl.getValue("get(0)", context, list); assertEquals("item1", first); } /** * Test with StringBuilder to verify method resolution on concrete classes */ @Test void stringBuilderMethodResolution() throws Exception { StringBuilder sb = new StringBuilder("hello"); OgnlContext context = Ognl.createDefaultContext(sb); Object len = Ognl.getValue("length()", context, sb); assertEquals(5, len); Object str = Ognl.getValue("toString()", context, sb); assertEquals("hello", str); } /** * Test getMethods to ensure it returns methods from interfaces and classes * This helps test the method collection logic that feeds into findBestMethod */ @Test void getMethodsIncludesInterfaceAndClassMethods() throws Exception { // Test that getMethods returns methods from both the class and its interfaces Class clazz = HashMap.class; // Get methods - this uses OgnlRuntime.getMethods internally Method[] methods = clazz.getMethods(); // Should have methods from Map interface boolean hasMapMethods = false; boolean hasHashMapMethods = false; for (Method m : methods) { if (m.getName().equals("get") && m.getDeclaringClass().isInterface()) { hasMapMethods = true; } if (m.getName().equals("get") && !m.getDeclaringClass().isInterface()) { hasHashMapMethods = true; } } // At least one source should provide the method assertTrue(hasMapMethods || hasHashMapMethods, "Should have get() method from either Map interface or HashMap class"); } /** * Test that verifies OGNL handles CharSequence interface correctly * String implements CharSequence, testing interface preference */ @Test void charSequenceInterfaceHandling() throws Exception { CharSequence seq = "test string"; OgnlContext context = Ognl.createDefaultContext(seq); Object len = Ognl.getValue("length()", context, seq); assertEquals(11, len); Object charAt = Ognl.getValue("charAt(0)", context, seq); assertEquals('t', charAt); } /** * Test method resolution with Comparable interface */ @Test void comparableInterfaceMethodResolution() throws Exception { Comparable str = "abc"; OgnlContext context = Ognl.createDefaultContext(str); Object result = Ognl.getValue("compareTo('abc')", context, str); assertEquals(0, result); Object length = Ognl.getValue("length()", context, str); assertEquals(3, length); } /** * Test that simulates the actual issue #286 scenario with a class in sun.* package *

* This test uses classes in sun.test package to simulate internal JDK classes. * The SimulatedInternalClass will be detected as inaccessible, while the * PublicTestInterface will be accessible, allowing us to test the preference logic. */ @Test void simulatedInternalClassVsInterface() throws Exception { // Create an instance of a class in "sun.test" package // This simulates sun.security.x509.X509CertImpl sun.test.PublicTestInterface obj = new sun.test.SimulatedInternalClass(); OgnlContext context = Ognl.createDefaultContext(obj); // OGNL should prefer the interface method over the class method // because the class is in a "sun." package Object result = Ognl.getValue("testMethod()", context, obj); assertNotNull(result); assertEquals("internal", result); } // Public interface - represents java.security.cert.X509Certificate public interface TestInterface { String publicMethod(); } // Internal implementation - represents sun.security.x509.X509CertImpl // In a real scenario, this would be a non-exported class from a JDK module public static class InternalImplementation implements TestInterface { @Override public String publicMethod() { return "result"; } } // Additional test interfaces and classes for comprehensive coverage public interface ParameterizedInterface { String process(String input); } public static class ParameterizedImplementation implements ParameterizedInterface { @Override public String process(String input) { return "processed: " + input; } } public interface FirstInterface { String getValue(); } public interface SecondInterface { String getValue(); } public static class MultiInterfaceImplementation implements FirstInterface, SecondInterface { @Override public String getValue() { return "multi"; } } public interface BaseInterface { String baseMethod(); } public static class PublicImplementation implements BaseInterface { @Override public String baseMethod() { return "public"; } } public interface ContainerInterface { TestInterface getChild(); } public static class ContainerImplementation implements ContainerInterface { @Override public TestInterface getChild() { return new InternalImplementation(); } } public interface OverloadedInterface { Integer compute(int a); Integer compute(int a, int b); } public static class OverloadedImplementation implements OverloadedInterface { @Override public Integer compute(int a) { return a * 2; } @Override public Integer compute(int a, int b) { return a + b; } } public interface AbstractInterface { String abstractMethod(); } public abstract static class AbstractBase implements AbstractInterface { public abstract String abstractMethod(); } public static class ConcreteImplementation extends AbstractBase { @Override public String abstractMethod() { return "concrete"; } } public interface GenericInterface { String transform(T input); } public static class GenericImplementation implements GenericInterface { @Override public String transform(String input) { return "TRANSFORMED: " + input; } } public interface CollectionInterface { List getItems(); } public static class CollectionImplementation implements CollectionInterface { @Override public List getItems() { return List.of("item1", "item2", "item3"); } } public interface ParentInterface { String baseMethod(); } public interface ExtendedInterface extends ParentInterface { String extendedMethod(); } public static class ExtendedImplementation implements ExtendedInterface { @Override public String baseMethod() { return "base"; } @Override public String extendedMethod() { return "extended"; } } public interface FactoryInterface { TestInterface create(); } public static class FactoryImplementation implements FactoryInterface { @Override public TestInterface create() { return new InternalImplementation(); } } public interface NullableInterface { String handleNull(String input); } public static class NullableImplementation implements NullableInterface { @Override public String handleNull(String input) { return input == null ? "null handled" : "value: " + input; } } public interface PrimitiveInterface { int add(int a, int b); } public static class PrimitiveImplementation implements PrimitiveInterface { @Override public int add(int a, int b) { return a + b; } } // Package-private class to test preference for accessible methods static class PackagePrivateImpl { public String publicMethod() { return "package-private"; } } // Interfaces for testing multiple inheritance public interface InterfaceA { String methodA(); } public interface InterfaceB { String methodB(); } public interface CombinedInterface extends InterfaceA, InterfaceB { } public static class CombinedImplementation implements CombinedInterface { @Override public String methodA() { return "A"; } @Override public String methodB() { return "B"; } } } ================================================ FILE: ognl/src/test/java/ognl/test/Issue472CustomMethodAccessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test case for GitHub issue #472: Root properties not accessible from lambda expressions. *

* This is a follow-up to issue #390, specifically testing the scenario where: * 1. Lambda expressions are used in list operations (selection, projection) * 2. The lambda is evaluated via Ognl.getValue() internally with list items as root * 3. The lambda needs to access properties from the ORIGINAL context root, not the list item *

* The bug occurred because Ognl.getValue() would call context.withRoot(root) which * created a new context and overwrote the root with the current evaluation target (list item), * making the original root properties inaccessible. *

* The fix removes the context.withRoot(root) call from getValue(), using the context directly * without modification, thus preserving the original root throughout the evaluation. * * @see Issue #472 */ class Issue472CustomMethodAccessorTest { private OgnlContext context; private TestRootObject rootObject; public static class TestRootObject { private String targetValue = "value"; private final String prefix = "test_"; private final int minLength = 6; private final int maxLength = 11; private final List testList = Arrays.asList("value", "other", "different"); private final List prefixedList = Arrays.asList("test_value", "other", "test_item"); private final List lengthList = Arrays.asList("short", "medium_size", "very_long_string"); public String getTargetValue() { return targetValue; } public String getPrefix() { return prefix; } public int getMinLength() { return minLength; } public int getMaxLength() { return maxLength; } public List getTestList() { return testList; } public List getPrefixedList() { return prefixedList; } public List getLengthList() { return lengthList; } } @BeforeEach void setUp() { rootObject = new TestRootObject(); context = Ognl.createDefaultContext(rootObject); } @Test void testRootPropertyAccessInListSelection() throws OgnlException { // Test that list selection can access root properties // This tests the core Issue #472: root properties must be accessible when // Ognl.getValue() is called with list items as the root parameter String expression = "testList.{? #this.equals(#root.targetValue)}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertInstanceOf(List.class, result, "Result should be a List"); List resultList = (List) result; assertEquals(1, resultList.size(), "Should find one matching item"); assertEquals("value", resultList.get(0)); } @Test void testRootPropertyAccessWithNonMatchingValue() throws OgnlException { // Test that list selection returns empty when root property doesn't match any items rootObject.targetValue = "nonexistent"; String expression = "testList.{? #this.equals(#root.targetValue)}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertInstanceOf(List.class, result); List resultList = (List) result; assertEquals(0, resultList.size(), "Should find no matching items"); } @Test void testLambdaAccessingBothRootAndListItem() throws OgnlException { // Test that expressions can access both #root properties and #this (the list item) // This verifies that the fix preserves context root while still allowing access to the current item String expression = "prefixedList.{? #this.startsWith(#root.prefix)}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertInstanceOf(List.class, result); List resultList = (List) result; assertEquals(2, resultList.size(), "Should find two items with prefix"); assertEquals("test_value", resultList.get(0)); assertEquals("test_item", resultList.get(1)); } @Test void testMultipleRootPropertiesInExpression() throws OgnlException { // Test accessing multiple root properties from within list selection expression // This validates that the entire root object remains accessible, not just a single property String expression = "lengthList.{? #this.length() >= #root.minLength && #this.length() <= #root.maxLength}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertInstanceOf(List.class, result); List resultList = (List) result; assertEquals(1, resultList.size(), "Should find one item within length range"); assertEquals("medium_size", resultList.get(0)); } @Test void testListProjectionWithRootAccess() throws OgnlException { // Test that list projection can also access root properties String expression = "testList.{#this + '-' + #root.targetValue}"; Object result = Ognl.getValue(expression, context, rootObject); assertNotNull(result); assertInstanceOf(List.class, result); List resultList = (List) result; assertEquals(3, resultList.size(), "Should project all items with root value appended"); assertEquals("value-value", resultList.get(0)); assertEquals("other-value", resultList.get(1)); assertEquals("different-value", resultList.get(2)); } } ================================================ FILE: ognl/src/test/java/ognl/test/Java8Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class Java8Test> { @Test void testDefaultMethodOnClass() { /* defaultMethod(); */ List defaultMethod = OgnlRuntime.getMethods(ClassWithDefaults.class, "defaultMethod", false); assertNotNull(defaultMethod); Method method = OgnlRuntime.getReadMethod(ClassWithDefaults.class, "defaultMethod"); assertNotNull(method); } @Test void testDefaultMethodOnSubClass() { /* defaultMethod(); */ List defaultMethod = OgnlRuntime.getMethods(SubClassWithDefaults.class, "defaultMethod", false); assertNotNull(defaultMethod); Method method = OgnlRuntime.getReadMethod(SubClassWithDefaults.class, "defaultMethod"); assertNotNull(method); } @Test void testGetDeclaredMethods() { List defaultMethod = OgnlRuntime.getDeclaredMethods(SubClassWithDefaults.class, "name", false); assertNotNull(defaultMethod); defaultMethod = OgnlRuntime.getDeclaredMethods(ClassWithDefaults.class, "name", false); assertNotNull(defaultMethod); } @Test void testAccessingDefaultMethod() throws OgnlException { ClassWithDefaults root = new ClassWithDefaults(); Object value = Ognl.getValue("name", Ognl.createDefaultContext(root), root); assertEquals("name", value); } } class SubClassWithDefaults extends ClassWithDefaults { public String getName() { return "name"; } } class ClassWithDefaults implements SubInterfaceWithDefaults { } interface InterfaceWithDefaults { default void defaultMethod() { } default String getName() { return "name"; } } interface SubInterfaceWithDefaults extends InterfaceWithDefaults { } ================================================ FILE: ognl/src/test/java/ognl/test/LambdaExpressionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.SimpleNode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigInteger; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; class LambdaExpressionTest { private OgnlContext context; @BeforeEach void setUp() throws Exception { this.context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); } private SimpleNode getExpression(Object root, String expressionStr) throws Exception { // validate expression Ognl.parseExpression(expressionStr); // compile expression return (SimpleNode) Ognl.compileExpression(context, root, expressionStr); } @Test void shouldReadArrayLength() throws Exception { // given Object root = new Object[]{}; String expressionStr = "#a=:[33](20).longValue().{0}.toArray().length"; int expectedResult = 33; // when SimpleNode expression = getExpression(root, expressionStr); // then assertEquals(expectedResult, Ognl.getValue(expression, context, root)); } @Test void shouldEvaluateLambda1() throws Exception { // given Object root = null; String expressionStr = "#fact=:[#this <=1 ? 1 : #fact(#this-1) * #this], #fact(30)"; int expectedResult = 1409286144; // when SimpleNode expression = getExpression(root, expressionStr); // then assertEquals(expectedResult, Ognl.getValue(expression, context, root)); } @Test void shouldEvaluateLambda2() throws Exception { // given Object root = null; String expressionStr = "#fact=:[#this <= 1 ? 1 : #fact(#this-1) * #this], #fact(30L)"; long expectedResult = -8764578968847253504L; // when SimpleNode expression = getExpression(root, expressionStr); // then assertEquals(expectedResult, Ognl.getValue(expression, context, root)); } @Test void shouldEvaluateLambda3() throws Exception { // given Object root = null; String expressionStr = "#fact=:[#this <= 1 ? 1 : #fact(#this-1) * #this], #fact(30h)"; BigInteger expectedResult = new BigInteger("265252859812191058636308480000000"); // when SimpleNode expression = getExpression(root, expressionStr); // then assertEquals(expectedResult, Ognl.getValue(expression, context, root)); } @Test void shouldEvaluateLambda4() throws Exception { // given Object root = null; String expressionStr = "#bump = :[ #this.{ #this + 1 } ], (#bump)({ 1, 2, 3 })"; List expectedResult = Arrays.asList(2, 3, 4); // when SimpleNode expression = getExpression(root, expressionStr); // then assertEquals(expectedResult, Ognl.getValue(expression, context, root)); } @Test void shouldEvaluateLambda5() throws Exception { // given Object root = null; String expressionStr = "#call = :[ \"calling \" + [0] + \" on \" + [1] ], (#call)({ \"x\", \"y\" })"; String expectedResult = "calling x on y"; // when SimpleNode expression = getExpression(root, expressionStr); // then assertEquals(expectedResult, Ognl.getValue(expression, context, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/MapCreationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; import static org.junit.jupiter.api.Assertions.assertEquals; class MapCreationTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root); } @Test void testMapCreation1() throws Exception { Map expected = Map.of("foo", "bar"); Object actual = Ognl.getValue("#{ \"foo\" : \"bar\" }", context, root); assertEquals(expected, actual); } @Test void testMapCreation2() throws Exception { Map expected = Map.of( "foo", "bar", "bar", "baz" ); Object actual = Ognl.getValue("#{ \"foo\" : \"bar\", \"bar\" : \"baz\" }", context, root); assertEquals(expected, actual); } @Test void testMapCreation3() throws Exception { Map expected = new HashMap<>(); expected.put("foo", null); expected.put("bar", "baz"); Object actual = Ognl.getValue("#{ \"foo\", \"bar\" : \"baz\" }", context, root); assertEquals(expected, actual); } @Test void testMapCreation4() throws Exception { Map expected = new LinkedHashMap<>(); expected.put("foo", "bar"); expected.put("bar", "baz"); Object actual = Ognl.getValue("#@java.util.LinkedHashMap@{ \"foo\" : \"bar\", \"bar\" : \"baz\" }", context, root); assertEquals(expected, actual); } @Test void testMapCreation5() throws Exception { Map expected = new TreeMap<>(); expected.put("foo", "bar"); expected.put("bar", "baz"); Object actual = Ognl.getValue("#@java.util.TreeMap@{ \"foo\" : \"bar\", \"bar\" : \"baz\" }", context, root); assertEquals(expected, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/MemberAccessTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import java.lang.reflect.Member; import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class MemberAccessTest { private Simple root; private OgnlContext context; @ParameterizedTest() @ValueSource( strings = {"@Runtime@getRuntime()", "bigIntValue", "getBigIntValue()"} ) void shouldBlockAccessReadToSpecificProperties(String expression) { assertThrows(OgnlException.class, () -> Ognl.getValue(expression, context, root), ""); } @Test void shouldBlockAccessOnWriteToSpecificProperties() { assertThrows(OgnlException.class, () -> Ognl.setValue("bigIntValue", context, root, 25), ""); } @Test void shouldAllowAccessToOtherProperties() throws Exception { Object actual = Ognl.getValue("@System@getProperty('java.specification.version')", context, root); assertEquals(System.getProperty("java.specification.version"), actual); assertEquals(root.getStringValue(), Ognl.getValue("stringValue", context, root)); } @BeforeEach public void setUp() { /* Should allow access at all to the Simple class except for the bigIntValue property */ DefaultMemberAccess ma = new DefaultMemberAccess(false) { public boolean isAccessible(OgnlContext context, Object target, Member member, String propertyName) { if (target == Runtime.class) { return false; } if (target instanceof Simple) { if (propertyName != null) { return !propertyName.equals("bigIntValue") && super.isAccessible(context, target, member, propertyName); } else { if (member instanceof Method) { return !member.getName().equals("getBigIntValue") && !member.getName().equals("setBigIntValue") && super.isAccessible(context, target, member, null); } } } return super.isAccessible(context, target, member, propertyName); } }; root = new Simple(); context = Ognl.createDefaultContext(root, ma); } } ================================================ FILE: ognl/src/test/java/ognl/test/MethodTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.BaseGeneric; import ognl.test.objects.GameGeneric; import ognl.test.objects.GameGenericObject; import ognl.test.objects.ListSource; import ognl.test.objects.ListSourceImpl; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; class MethodTest { private Simple root; private ListSource list; private BaseGeneric generic; private OgnlContext context; @BeforeEach void setUp() { root = new Simple(); list = new ListSourceImpl(); generic = new GameGeneric(); context = Ognl.createDefaultContext(root); } @Test void testHashCode() throws Exception { Object actual = Ognl.getValue("hashCode()", context, root); assertEquals(root.hashCode(), actual); } @Test void testGetBooleanValue() throws Exception { Object actual = Ognl.getValue("getBooleanValue() ? \"here\" : \"\"", context, root); assertEquals("", actual); } @Test void testGetValueIsTrue() throws Exception { Object actual = Ognl.getValue("getValueIsTrue(!false) ? \"\" : \"here\" ", context, root); assertEquals("", actual); } @Test void testMessagesFormatShowAllCountOne() throws Exception { Object actual = Ognl.getValue("messages.format('ShowAllCount', one)", context, root); assertEquals(root.getMessages().format("ShowAllCount", root.getOne()), actual); } @Test void testMessagesFormatShowAllCountArrayOne() throws Exception { Object actual = Ognl.getValue("messages.format('ShowAllCount', {one})", context, root); assertEquals(root.getMessages().format("ShowAllCount", new Object[]{root.getOne()}), actual); } @Test void testMessagesFormatShowAllCountArrayOneTwo() throws Exception { Object actual = Ognl.getValue("messages.format('ShowAllCount', {one, two})", context, root); assertEquals(root.getMessages().format("ShowAllCount", new Object[]{root.getOne(), root.getTwo()}), actual); } @Test void testMessagesFormatShowAllCountOneTwo() throws Exception { Object actual = Ognl.getValue("messages.format('ShowAllCount', one, two)", context, root); assertEquals(root.getMessages().format("ShowAllCount", root.getOne(), root.getTwo()), actual); } @Test void testGetTestValue() throws Exception { Object actual = Ognl.getValue("getTestValue(@ognl.test.objects.SimpleEnum@ONE.value)", context, root); assertEquals(2, actual); } @Test void testGetAIsProperty() throws Exception { Object actual = Ognl.getValue("@ognl.test.MethodTest@getA().isProperty()", context, root); assertEquals(Boolean.FALSE, actual); } @Test void testIsDisabled() throws Exception { Object actual = Ognl.getValue("isDisabled()", context, root); assertEquals(Boolean.TRUE, actual); } @Test void testIsTruck() throws Exception { assertEquals(Boolean.TRUE, Ognl.getValue("isTruck", context, root)); } @Test void testIsEditorDisabled() throws Exception { Object actual = Ognl.getValue("isEditorDisabled()", context, root); assertEquals(Boolean.FALSE, actual); } @Test void testListAddValue() throws Exception { Object actual = Ognl.getValue("addValue(name)", context, list); assertEquals(Boolean.TRUE, actual); } @Test void testGetDisplayValue() throws Exception { Object actual = Ognl.getValue("getDisplayValue(methodsTest.allowDisplay)", context, root); assertEquals("test", actual); } @Test void testIsThisVarArgsWorkingWithArgs() throws Exception { Object actual = Ognl.getValue("isThisVarArgsWorking(three, rootValue)", context, root); assertEquals(Boolean.TRUE, actual); } @Test void testIsThisVarArgsWorkingWithoutArgs() throws Exception { Object actual = Ognl.getValue("isThisVarArgsWorking()", context, root); assertEquals(Boolean.TRUE, actual); } @Test void testGenericServiceGetFullMessageFor() throws Exception { Object actual = Ognl.getValue("service.getFullMessageFor(value, null)", context, generic); assertEquals("Halo 3", actual); } @Test void testTestMethodsGetBean() throws Exception { Object actual = Ognl.getValue("testMethods.getBean('TestBean')", context, root); assertEquals(root.getTestMethods().getBean("TestBean"), actual); } @Test void testTestMethodsTestProperty() throws Exception { Object actual = Ognl.getValue("testMethods.testProperty", context, root); assertEquals(root.getTestMethods().testProperty(), actual); } @Test void testTestMethodsArgsTest1() throws Exception { Object actual = Ognl.getValue("testMethods.argsTest1({one})", context, root); assertEquals(root.getTestMethods().argsTest1(List.of(root.getOne()).toArray()), actual); } @Test void testTestMethodsArgsTest2() throws Exception { Object actual = Ognl.getValue("testMethods.argsTest2({one})", context, root); assertEquals(root.getTestMethods().argsTest2(List.of(root.getOne())), actual); } @Test void testTestMethodsArgsTest3() throws Exception { Object actual = Ognl.getValue("testMethods.argsTest3({one})", context, root); assertEquals("List: [1]", actual); } @Test void testTestMethodsShowListObjectList() throws Exception { Object actual = Ognl.getValue("testMethods.showList(testMethods.getObjectList())", context, root); assertEquals(root.getTestMethods().showList(root.getTestMethods().getObjectList().toArray()), actual); } @Test void testTestMethodsShowListStringList() throws Exception { Object actual = Ognl.getValue("testMethods.showList(testMethods.getStringList())", context, root); assertEquals(root.getTestMethods().showList(root.getTestMethods().getStringList().toArray()), actual); } @Test void testTestMethodsShowListStringArray() throws Exception { Object actual = Ognl.getValue("testMethods.showList(testMethods.getStringArray())", context, root); assertEquals(root.getTestMethods().showList(root.getTestMethods().getStringArray()), actual); } @Test void testTestMethodsShowStringList() throws Exception { Object actual = Ognl.getValue("testMethods.showStringList(testMethods.getStringList().toArray(new String[0]))", context, root); assertEquals(root.getTestMethods().showStringList(root.getTestMethods().getStringList().toArray(new String[0])), actual); } @Test void testTestMethodsAvg() throws Exception { Object actual = Ognl.getValue("testMethods.avg({ 5, 5 })", context, root); assertEquals(root.getTestMethods().avg(Arrays.asList(5, 5)), actual); } @Test void testNullVarArgs() throws OgnlException { Object value = Ognl.getValue("isThisVarArgsWorking()", context, root); assertInstanceOf(Boolean.class, value); assertTrue((Boolean) value); } public static class A { public boolean isProperty() { return false; } } public static A getA() { return new A(); } } ================================================ FILE: ognl/src/test/java/ognl/test/MethodWithConversionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class MethodWithConversionTest { private Simple simple; private OgnlContext context; @BeforeEach void setUp() { simple = new Simple(); context = Ognl.createDefaultContext(simple); } @Test void testSetValues() throws Exception { Ognl.getValue("setValues(10, \"10.56\", 34.225D)", context, simple); assertEquals("10", simple.getStringValue()); assertEquals(10.56F, simple.getFloatValue()); assertEquals(34, simple.getIntValue()); } @Test void testStringValue() throws Exception { Ognl.getValue("setValues(10, \"10.56\", 34.225D)", context, simple); assertEquals("10", Ognl.getValue("stringValue", context, simple)); assertEquals(10.56F, Ognl.getValue("floatValue", context, simple)); assertEquals(34, Ognl.getValue("intValue", context, simple)); } @Test void testStringValueWithChar() throws Exception { Ognl.setValue("stringValue", context, simple, 'x'); assertEquals("x", Ognl.getValue("stringValue", context, simple)); } @Test void testSetStringValue() throws Exception { Ognl.getValue("setStringValue('x')", context, simple); assertEquals("x", Ognl.getValue("stringValue", context, simple)); } @Test void testFloatValue() throws Exception { Ognl.getValue("setValues(10, \"10.56\", 34.225D)", context, simple); assertEquals(10.56f, Ognl.getValue("floatValue", context, simple)); } @Test void testGetValueIsTrue() throws Exception { Object actual = Ognl.getValue("getValueIsTrue(rootValue)", context, simple); assertEquals(Boolean.TRUE, actual); } @Test void testMessagesFormat() throws Exception { Object actual = Ognl.getValue("messages.format('Testing', one, two, three)", context, simple); assertEquals("blah", actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/NestedMethodTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Component; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class NestedMethodTest { private Component component; private OgnlContext context; @BeforeEach void setUp() { component = new Component(); context = Ognl.createDefaultContext(component); } @Test void testToDisplayPictureUrl() throws Exception { Object actual = Ognl.getValue("toDisplay.pictureUrl", context, component); assertEquals(component.getToDisplay().getPictureUrl(), actual); } @Test void testPageCreateRelativeAsset() throws Exception { Object actual = Ognl.getValue("page.createRelativeAsset(toDisplay.pictureUrl)", context, component); assertEquals(component.getPage().createRelativeAsset(component.getToDisplay().getPictureUrl()), actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/NullHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlRuntime; import ognl.test.objects.CorrectedObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; class NullHandlerTest { private CorrectedObject root; @ParameterizedTest() @CsvSource({ "stringValue,corrected", "getStringValue(),corrected", "#root.stringValue,corrected", "#root.getStringValue(),corrected" }) void shouldBlockAccessReadToSpecificProperties(String expression, String expected) throws Exception { assertEquals(expected, Ognl.getValue(expression, root)); } @BeforeEach public void setUp() { OgnlRuntime.setNullHandler(CorrectedObject.class, new CorrectedObjectNullHandler("corrected")); this.root = new CorrectedObject(); } } ================================================ FILE: ognl/src/test/java/ognl/test/NullRootTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertNull; class NullRootTest { @Test void testNullValue() throws Exception { OgnlContext context = Ognl.createDefaultContext(null); Map root = new HashMap<>(); root.put("key1", null); String expr = "key1.key2.key3"; assertNull(Ognl.getValue(expr, context, root)); } @Test void testEmptyRoot() throws Exception { OgnlContext context = Ognl.createDefaultContext(null); Map root = new HashMap<>(); String expr = "key1.key2.key3"; assertNull(Ognl.getValue(expr, context, root)); } @Test void testNullRoot() throws Exception { OgnlContext context = Ognl.createDefaultContext(null); Map root = null; String expr = "key1.key2.key3"; assertNull(Ognl.getValue(expr, context, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/NullSafeCollectionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; /** * Tests for null-safe operator (?.) with collections, arrays, and dynamic subscripts. */ class NullSafeCollectionTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } @ParameterizedTest @ValueSource(strings = { "list?.{name}", "list?.{? #this.active}", "items?.{? #this > 0}" }) void nullSafeExpressionParsing(String expression) { assertDoesNotThrow(() -> { Object expr = Ognl.parseExpression(expression); assertNotNull(expr); }); } @ParameterizedTest @MethodSource("projectionSelectionTestCases") void nullSafeProjectionAndSelection(String expression, Object value, boolean expectNull) throws Exception { Map root = new HashMap<>(); root.put("items", value); Object result = Ognl.getValue(expression, context, root); if (expectNull) { assertNull(result); } else { assertNotNull(result); } } static Stream projectionSelectionTestCases() { List> users = Arrays.asList( createMap("name", "Alice"), createMap("name", "Bob") ); return Stream.of( Arguments.of("items?.{name}", null, true), Arguments.of("items.{name}", users, false), Arguments.of("items?.{? #this > 0}", null, true) ); } @ParameterizedTest @MethodSource("propertyAccessTestCases") void nullSafePropertyAccess(String expression, Object value, Object expected) throws Exception { Map root = new HashMap<>(); root.put("items", value); Object result = Ognl.getValue(expression, context, root); assertEquals(expected, result); } static Stream propertyAccessTestCases() { return Stream.of( Arguments.of("items?.length", new String[]{"a", "b", "c"}, 3), Arguments.of("items?.length", null, null), Arguments.of("items?.size()", Arrays.asList("a", "b", "c"), 3), Arguments.of("items?.size()", null, null) ); } @ParameterizedTest @MethodSource("dynamicSubscriptTestCases") void dynamicSubscriptAccess(String expression, String expected) throws Exception { Map root = new HashMap<>(); String[] items = {"first", "second", "third"}; root.put("items", items); Object result = Ognl.getValue(expression, context, root); assertEquals(expected, result); } static Stream dynamicSubscriptTestCases() { return Stream.of( Arguments.of("items[^]", "first"), // [^] gets first element Arguments.of("items[$]", "third"), // [$] gets last element Arguments.of("items[0]", "first") // Regular index access ); } private static Map createMap(String key, String value) { Map map = new HashMap<>(); map.put(key, value); return map; } } ================================================ FILE: ognl/src/test/java/ognl/test/NullSafeCompilationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlRuntime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; /** * Tests for null-safe operator (?.) expression compilation and toString() functionality. */ class NullSafeCompilationTest { private OgnlContext context; static class User { private String name; private Profile profile; User(String name, Profile profile) { this.name = name; this.profile = profile; } public String getName() { return name; } public Profile getProfile() { return profile; } } static class Profile { private String bio; private Address address; Profile(String bio, Address address) { this.bio = bio; this.address = address; } public String getBio() { return bio; } public Address getAddress() { return address; } } static class Address { private String city; Address(String city) { this.city = city; } public String getCity() { return city; } } @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } @Test void nullSafeWithCompiledExpression() throws Exception { User user = new User("Alice", null); Object expr = Ognl.parseExpression("profile?.bio"); try { Object compiled = Ognl.compileExpression(context, user, "profile?.bio"); assertNotNull(compiled, "Compiled expression should not be null"); } catch (Exception e) { fail(e.getMessage()); } Object result = Ognl.getValue(expr, context, user); assertNull(result); } @Test void nullSafeToGetSourceString() throws Exception { Object expr = Ognl.parseExpression("profile?.address?.city"); try { String source = OgnlRuntime.getCompiler().getClassName(expr.getClass()); assertNotNull(source); } catch (Exception e) { fail(e.getMessage()); } } @ParameterizedTest @MethodSource("toStringTestCases") void expressionToString(String expression, String expectedSubstring) throws Exception { Object expr = Ognl.parseExpression(expression); String exprString = expr.toString(); assertNotNull(exprString, "Expression string should not be null"); assertTrue(exprString.contains(expectedSubstring), "Expression string should contain '" + expectedSubstring + "'"); } static Stream toStringTestCases() { return Stream.of( Arguments.of("a?.b?.c", "?"), Arguments.of("user?.profile", "user"), Arguments.of("user?.name", "user") ); } @Test void toStringComplexExpression() throws Exception { Object expr = Ognl.parseExpression("user?.profile?.address?.city"); String exprString = expr.toString(); // Should contain multiple null-safe operators int questionMarks = exprString.length() - exprString.replace("?", "").length(); assertTrue(questionMarks >= 3, "Expression should contain multiple null-safe operators"); } } ================================================ FILE: ognl/src/test/java/ognl/test/NullSafeIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; /** * Integration tests for null-safe operator (?.) including edge cases, * combined operators, and interaction with other OGNL features. */ class NullSafeIntegrationTest { private OgnlContext context; static class User { private String name; private Profile profile; User(String name, Profile profile) { this.name = name; this.profile = profile; } public String getName() { return name; } public Profile getProfile() { return profile; } } static class Profile { private String bio; private Address address; private boolean verified; Profile(String bio, Address address) { this.bio = bio; this.address = address; } public String getBio() { return bio; } public Address getAddress() { return address; } public boolean isVerified() { return verified; } } static class Address { private String city; private String street; Address(String city, String street) { this.city = city; this.street = street; } public String getCity() { return city; } public String getStreet() { return street; } } @BeforeEach void setUp() { context = (OgnlContext) Ognl.createDefaultContext(null); } // ========== Combined Operator Tests ========== @ParameterizedTest @MethodSource("conditionalOperatorTestCases") void nullSafeWithConditionalOperators(String expression, Object expected) throws Exception { User user = new User("Alice", null); Object result = Ognl.getValue(expression, context, user); assertEquals(expected, result); } static Stream conditionalOperatorTestCases() { return Stream.of( Arguments.of("profile?.bio != null ? profile?.bio : 'default'", "default"), Arguments.of("profile?.bio != null ? 'yes' : 'no'", "no") ); } @Test void nullSafeInAssignmentContext() throws Exception { User user = new User("Alice", null); Ognl.getValue("#result = profile?.bio", context, user); assertNull(context.get("result")); } @Test void nullSafeWithArithmetic() throws Exception { Map root = new HashMap<>(); root.put("value", null); // Use ternary operator since OGNL doesn't have ?: Elvis operator Object result = Ognl.getValue("(value != null ? value : 0) + 10", context, root); assertEquals(10, result); } // ========== Edge Cases ========== @ParameterizedTest @ValueSource(strings = { "profile?.address?.city", "profile?.address?.city?.toString()", "profile?.address?.city?.toString()?.toLowerCase()?.substring(0)?.trim()?.length()?.toString()?.isEmpty()" }) void deepNullSafeChains(String expression) throws Exception { User user = new User("Alice", null); Object result = Ognl.getValue(expression, context, user); assertNull(result, "Deep null-safe chain should return null if any intermediate is null"); } @Test void nullSafeOnLiterals() throws Exception { // Literals are never null, so this should work Object result = Ognl.getValue("'hello'.toString()", context, new Object()); assertEquals("hello", result); } @Test void nullSafeOnStaticMethod() throws Exception { // Static methods combined with null-safe property access Object result = Ognl.getValue("@java.lang.System@getProperty('java.version')?.toString()", context, new Object()); assertNotNull(result); } @Test void nullSafeWithThis() throws Exception { Map root = new HashMap<>(); root.put("obj", null); Object result = Ognl.getValue("#obj?.toString()", context, new Object()); assertNull(result); } @Test void nullSafeWithRoot() throws Exception { Object result = Ognl.getValue("#root?.toString()", context, (Object) null); assertNull(result); } // ========== Integration Tests ========== @Test void nullSafeDoesNotAffectRegularAccess() throws Exception { User user = new User("Alice", new Profile("Bio", new Address("NYC", "5th Ave"))); // Regular access should still work Object result1 = Ognl.getValue("profile.bio", context, user); assertEquals("Bio", result1); // Null-safe access should also work Object result2 = Ognl.getValue("profile?.bio", context, user); assertEquals("Bio", result2); } @Test void nullSafeBackwardCompatibility() throws Exception { // Ensure existing expressions without ?. still work User user = new User("Alice", new Profile("Bio", new Address("NYC", "5th Ave"))); Object result = Ognl.getValue("profile.address.city", context, user); assertEquals("NYC", result); } @Test void nullSafeIndependentOfShortCircuit() throws Exception { // Null-safe should work regardless of short-circuit system property User user = new User("Alice", null); // With null-safe, should always return null Object result = Ognl.getValue("profile?.bio", context, user); assertNull(result); } // ========== Intermediate Null Check Tests ========== @Test void nullSafeWithIntermediateNull() throws Exception { // Create a chain where intermediate value becomes null User user = new User("Alice", new Profile(null, null)); // Access through multiple levels where intermediate is null Object result = Ognl.getValue("profile?.address?.city", context, user); assertNull(result); } @Test void nullSafeChainWithMultipleNullChecks() throws Exception { // Test that the loop's null check (line 94-95 in ASTChain) is hit User user = new User("Alice", null); // This creates a chain that needs multiple null checks Object result = Ognl.getValue("profile?.address?.city?.toString()", context, user); assertNull(result); } @Test void nullSafeWithNestedPropertyAccess() throws Exception { // Create nested structure to test intermediate null handling User user = new User("Alice", new Profile("Bio", new Address(null, "Street"))); Object result = Ognl.getValue("profile?.address?.city?.length()", context, user); assertNull(result); } } ================================================ FILE: ognl/src/test/java/ognl/test/NullSafeOperatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; /** * Comprehensive test suite for the null-safe navigation operator (?.) * Ensures 100% code coverage for the null-safe operator feature. */ class NullSafeOperatorTest { private OgnlContext context; // Test data classes public static class User { private String name; private Profile profile; private List tags; public User(String name, Profile profile) { this.name = name; this.profile = profile; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Profile getProfile() { return profile; } public void setProfile(Profile profile) { this.profile = profile; } public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } } public static class Profile { private String bio; private Address address; public Profile(String bio, Address address) { this.bio = bio; this.address = address; } public String getBio() { return bio; } public void setBio(String bio) { this.bio = bio; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } public static class Address { private String city; private String street; public Address(String city, String street) { this.city = city; this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } } @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } @Test void nullRootWithNullSafeOperator() throws Exception { // Null root returns null due to short-circuit behavior Object result = Ognl.getValue("#root", context, (Object) null); assertNull(result, "Null root should return null"); } @Test void nullSafeOnNullRoot() throws Exception { Object result = Ognl.getValue("#root?.name", context, (Object) null); assertNull(result, "Null-safe operator on null root should return null"); } @ParameterizedTest @MethodSource("successfulNavigationTestCases") void successfulNavigation(String expression, Object expected) throws Exception { User user = new User("Alice", new Profile("Bio", new Address("NYC", "5th Ave"))); Object result = Ognl.getValue(expression, context, user); assertEquals(expected, result); } static Stream successfulNavigationTestCases() { return Stream.of( Arguments.of("name", "Alice"), Arguments.of("getProfile()?.getAddress()?.getCity()", "NYC"), Arguments.of("profile?.getAddress()?.city", "NYC") ); } @Test void nullSafeOnNullProperty() throws Exception { User user = new User("Alice", null); Object result = Ognl.getValue("profile?.bio", context, user); assertNull(result, "Null-safe operator should return null when property is null"); } @ParameterizedTest @MethodSource("chainedNullSafeTestCases") void chainedNullSafeOperators(String expression, User user, Object expected, String description) throws Exception { Object result = Ognl.getValue(expression, context, user); if (expected == null) { assertNull(result, description); } else { assertEquals(expected, result, description); } } static Stream chainedNullSafeTestCases() { User userWithFullProfile = new User("Alice", new Profile("Bio", new Address("NYC", "5th Ave"))); User userWithNullAddress = new User("Alice", new Profile("Bio", null)); User userWithNullProfile = new User("Alice", null); return Stream.of( // Multiple null-safe operators with non-null chain Arguments.of("profile?.address?.city", userWithFullProfile, "NYC", "Multiple null-safe operators should work on non-null chain"), // Null-safe chain with null intermediate Arguments.of("profile?.address?.city", userWithNullAddress, null, "Null-safe chain should return null when intermediate value is null"), // Deep null-safe chain with null at start Arguments.of("profile?.address?.city", userWithNullProfile, null, "Deep null-safe chain should return null at first null encounter"), // Mixed safe and unsafe chain Arguments.of("profile.address?.city", userWithFullProfile, "NYC", "Mixed safe and unsafe chain should work on non-null values"), // Mixed chain with null unsafe part Arguments.of("profile.address?.city", userWithNullProfile, null, "Short-circuit behavior returns null for null intermediate value") ); } @Test void nullSafeMethodCallOnNull() throws Exception { Object result = Ognl.getValue("#root?.toString()", context, (Object) null); assertNull(result, "Null-safe method call on null should return null"); } @Test void nullSafeMethodCallOnNonNull() throws Exception { User user = new User("Alice", null); Object result = Ognl.getValue("getName()", context, user); assertEquals("Alice", result); } @Test void nullSafeMethodChainWithNullIntermediate() throws Exception { User user = new User("Alice", new Profile("Bio", null)); Object result = Ognl.getValue("getProfile()?.getAddress()?.getCity()", context, user); assertNull(result, "Null-safe method chain should return null when intermediate is null"); } @Test void methodWithArgumentsNullSafe() throws Exception { String str = "hello"; Object result = Ognl.getValue("substring(0, 2)", context, str); assertEquals("he", result); } @Test void nullSafeMethodWithArgumentsOnNull() throws Exception { Object result = Ognl.getValue("#root?.substring(0, 2)", context, (Object) null); assertNull(result, "Null-safe method with arguments on null should return null"); } @Test void nullSafeWithVariableReference() throws Exception { context.put("user", null); Object result = Ognl.getValue("#user?.name", context, new Object()); assertNull(result, "Null-safe operator on null variable should return null"); } @Test void nullSafeWithNonNullVariable() throws Exception { User user = new User("Alice", new Profile("Bio", new Address("NYC", "5th Ave"))); context.put("user", user); Object result = Ognl.getValue("#user?.name", context, new Object()); assertEquals("Alice", result); } @Test void nullSafeWithNestedVariables() throws Exception { User user = new User("Alice", null); context.put("user", user); Object result = Ognl.getValue("#user?.profile?.bio", context, new Object()); assertNull(result); } @Test void nullSafeMapAccess() throws Exception { Map root = new HashMap<>(); root.put("user", null); Object result = Ognl.getValue("user?.name", context, root); assertNull(result); } @Test void nullSafeMapAccessWithNonNullValue() throws Exception { Map root = new HashMap<>(); User user = new User("Alice", new Profile("Bio", new Address("NYC", "5th Ave"))); root.put("user", user); Object result = Ognl.getValue("user?.name", context, root); assertEquals("Alice", result); } @Test void nullSafeIndexedMapAccess() throws Exception { // Note: Null-safe with direct indexing map?.['key'] is not supported in Phase 1 // because indexing doesn't use dot notation. Instead test map property access. Map root = new HashMap<>(); root.put("map", null); Object result = Ognl.getValue("map", context, root); assertNull(result, "Null map value should be null"); } @ParameterizedTest @MethodSource("nullSafeTestCases") void nullSafeScenarios(String expression, Object root, Object expected) throws Exception { Object result = Ognl.getValue(expression, context, root); assertEquals(expected, result); } static Stream nullSafeTestCases() { User userWithFullProfile = new User("Alice", new Profile("Bio", new Address("NYC", "5th Ave"))); User userWithNullProfile = new User("Alice", null); User userWithProfileNoAddress = new User("Alice", new Profile("Bio", null)); return Stream.of( // Basic null-safe access Arguments.of("profile?.bio", userWithFullProfile, "Bio"), Arguments.of("profile?.bio", userWithNullProfile, null), Arguments.of("name", userWithFullProfile, "Alice"), // Nested null-safe access Arguments.of("profile?.address?.city", userWithFullProfile, "NYC"), Arguments.of("profile?.address?.city", userWithNullProfile, null), Arguments.of("profile?.address?.city", userWithProfileNoAddress, null), // Method calls Arguments.of("getName()", userWithFullProfile, "Alice"), Arguments.of("getProfile()?.getBio()", userWithFullProfile, "Bio"), Arguments.of("getProfile()?.getBio()", userWithNullProfile, null), // Mixed chains Arguments.of("profile.address?.city", userWithFullProfile, "NYC"), Arguments.of("profile?.address.city", userWithFullProfile, "NYC") ); } @Test void parserAcceptsDotQuestion() { // Just verify that the parser accepts the ?. syntax without throwing parse exception assertDoesNotThrow(() -> { Ognl.parseExpression("obj?.property"); }); } @Test void complexNullSafeExpression() { assertDoesNotThrow(() -> { Ognl.parseExpression("a?.b?.c?.d?.e?.f"); }); } } ================================================ FILE: ognl/src/test/java/ognl/test/NullStringCatenationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class NullStringCatenationTest { /** * It's used in test */ public static final String MESSAGE = "blarney"; private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root, new DefaultMemberAccess(true)); } @Test void testCatenateNullToString() throws Exception { Object actual = Ognl.getValue("\"bar\" + null", context, root); assertEquals("barnull", actual); } @Test void testCatenateNullObjectToString() throws Exception { Object actual = Ognl.getValue("\"bar\" + nullObject", context, root); assertEquals("barnull", actual); } @Test void testCatenateNullObjectToNumber() { assertThrows(NullPointerException.class, () -> Ognl.getValue("20.56 + nullObject", context, root), "nullObject"); } @Test void testConditionalCatenation() throws Exception { Object actual = Ognl.getValue("(true ? 'tabHeader' : '') + (false ? 'tabHeader' : '')", context, root); assertEquals("tabHeader", actual); } @Test void testConditionalCatenationWithInt() throws Exception { Object actual = Ognl.getValue("theInt == 0 ? '5%' : theInt + '%'", context, root); assertEquals("6%", actual); } @Test void testCatenateWidth() throws Exception { Object actual = Ognl.getValue("'width:' + width + ';'", context, root); assertEquals("width:238px;", actual); } @Test void testCatenateLongAndIndex() throws Exception { Object actual = Ognl.getValue("theLong + '_' + index", context, root); assertEquals("4_1", actual); } @Test void testCatenateWithStaticField() throws Exception { Object actual = Ognl.getValue("'javascript:' + @ognl.test.NullStringCatenationTest@MESSAGE", context, root); assertEquals("javascript:blarney", actual); } @Test void testConditionalCatenationWithMethodCall() throws Exception { Object actual = Ognl.getValue("printDelivery ? '' : 'javascript:deliverySelected(' + property.carrier + ',' + currentDeliveryId + ')'", context, root); assertEquals("", actual); } @Test void testCatenateBeanIdAndInt() throws Exception { Object actual = Ognl.getValue("bean2.id + '_' + theInt", context, root); assertEquals("1_6", actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/NumberFormatExceptionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.math.BigInteger; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; class NumberFormatExceptionTest { private Simple simple; private OgnlContext context; @BeforeEach void setUp() { simple = new Simple(); context = Ognl.createDefaultContext(simple); } @Test void testFloatValueValid() throws Exception { Ognl.setValue("floatValue", context, simple, 10f); assertEquals(10f, Ognl.getValue("floatValue", context, simple)); } @Test void testFloatValueInvalid() { assertThrows(OgnlException.class, () -> Ognl.setValue("floatValue", context, simple, "x10x") , "x10x"); } @Test void testIntValueValid() throws Exception { Ognl.setValue("intValue", context, simple, 34); Object actual = Ognl.getValue("intValue", context, simple); assertEquals(34, actual); } @Test void testIntValueInvalidString() { assertThrows(OgnlException.class, () -> Ognl.setValue("intValue", context, simple, "foobar")); } @Test void testIntValueEmptyString() { assertThrows(OgnlException.class, () -> Ognl.setValue("intValue", context, simple, "")); } @Test void testIntValueWhitespaceString() { assertThrows(OgnlException.class, () -> Ognl.setValue("intValue", context, simple, " \t")); } @Test void testIntValueValidWhitespaceString() throws Exception { Ognl.setValue("intValue", context, simple, " \t1234\t\t"); Object actual = Ognl.getValue("intValue", context, simple); assertEquals(1234, actual); } @Test void testBigIntValueValid() throws Exception { Ognl.setValue("bigIntValue", context, simple, BigInteger.valueOf(34)); Object actual = Ognl.getValue("bigIntValue", context, simple); assertEquals(BigInteger.valueOf(34), actual); } @Test void testBigIntValueNull() throws Exception { Ognl.setValue("bigIntValue", context, simple, null); Object actual = Ognl.getValue("bigIntValue", context, simple); assertNull(actual); } @Test void testBigIntValueEmptyString() { assertThrows(OgnlException.class, () -> Ognl.setValue("bigIntValue", context, simple, "")); } @Test void testBigIntValueInvalidString() { assertThrows(OgnlException.class, () -> Ognl.setValue("bigIntValue", context, simple, "foobar")); } @Test void testBigDecValueValid() throws Exception { Ognl.setValue("bigDecValue", context, simple, BigDecimal.valueOf(34.55)); Object actual = Ognl.getValue("bigDecValue", context, simple); assertEquals(BigDecimal.valueOf(34.55), actual); } @Test void testBigDecValueNull() throws Exception { Ognl.setValue("bigDecValue", context, simple, null); Object actual = Ognl.getValue("bigDecValue", context, simple); assertNull(actual); } @Test void testBigDecValueEmptyString() { assertThrows(OgnlException.class, () -> Ognl.setValue("bigDecValue", context, simple, "")); } @Test void testBigDecValueInvalidString() { assertThrows(OgnlException.class, () -> Ognl.setValue("bigDecValue", context, simple, "foobar")); } } ================================================ FILE: ognl/src/test/java/ognl/test/NumericConversionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.OgnlOps; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.math.BigInteger; import static org.junit.jupiter.api.Assertions.assertEquals; class NumericConversionTest { private void runTest(Object value, Class toClass, Object expectedValue, int scale) { Object result = OgnlOps.convertValue(value, toClass); if (scale >= 0) { double scalingFactor = Math.pow(10, scale); double v1 = ((Number) result).doubleValue() * scalingFactor; double v2 = ((Number) expectedValue).doubleValue() * scalingFactor; assertEquals((int) v1, (int) v2); } else { assertEquals(result, expectedValue); } } @Test void testIntegerConversions() { runTest("55", Integer.class, 55, -1); runTest(55, Integer.class, 55, -1); runTest(55.0, Integer.class, 55, -1); runTest(true, Integer.class, 1, -1); runTest((byte) 55, Integer.class, 55, -1); runTest((char) 55, Integer.class, 55, -1); runTest((short) 55, Integer.class, 55, -1); runTest(55L, Integer.class, 55, -1); runTest(55.0f, Integer.class, 55, -1); runTest(new BigInteger("55"), Integer.class, 55, -1); runTest(new BigDecimal("55"), Integer.class, 55, -1); } @Test void testDoubleConversions() { runTest("55.1234", Double.class, 55.1234, -1); runTest(55, Double.class, 55.0, -1); runTest(55.1234, Double.class, 55.1234, -1); runTest(true, Double.class, 1.0, -1); runTest((byte) 55, Double.class, 55.0, -1); runTest((char) 55, Double.class, 55.0, -1); runTest((short) 55, Double.class, 55.0, -1); runTest(55L, Double.class, 55.0, -1); runTest(55.1234f, Double.class, 55.1234, 4); runTest(new BigInteger("55"), Double.class, 55.0, -1); runTest(new BigDecimal("55.1234"), Double.class, 55.1234, -1); } @Test void testBooleanConversions() { runTest("true", Boolean.class, true, -1); runTest(55, Boolean.class, true, -1); runTest(55.0, Boolean.class, true, -1); runTest(true, Boolean.class, true, -1); runTest((byte) 55, Boolean.class, true, -1); runTest((char) 55, Boolean.class, true, -1); runTest((short) 55, Boolean.class, true, -1); runTest(55L, Boolean.class, true, -1); runTest(55.0f, Boolean.class, true, -1); runTest(new BigInteger("55"), Boolean.class, true, -1); runTest(new BigDecimal("55"), Boolean.class, true, -1); } @Test void testByteConversions() { runTest("55", Byte.class, (byte) 55, -1); runTest(55, Byte.class, (byte) 55, -1); runTest(55.0, Byte.class, (byte) 55, -1); runTest(true, Byte.class, (byte) 1, -1); runTest((byte) 55, Byte.class, (byte) 55, -1); runTest((char) 55, Byte.class, (byte) 55, -1); runTest((short) 55, Byte.class, (byte) 55, -1); runTest(55L, Byte.class, (byte) 55, -1); runTest(55.0f, Byte.class, (byte) 55, -1); runTest(new BigInteger("55"), Byte.class, (byte) 55, -1); runTest(new BigDecimal("55"), Byte.class, (byte) 55, -1); } @Test void testCharacterConversions() { runTest("55", Character.class, (char) 55, -1); runTest(55, Character.class, (char) 55, -1); runTest(55.0, Character.class, (char) 55, -1); runTest(true, Character.class, (char) 1, -1); runTest((byte) 55, Character.class, (char) 55, -1); runTest((char) 55, Character.class, (char) 55, -1); runTest((short) 55, Character.class, (char) 55, -1); runTest(55L, Character.class, (char) 55, -1); runTest(55.0f, Character.class, (char) 55, -1); runTest(new BigInteger("55"), Character.class, (char) 55, -1); runTest(new BigDecimal("55"), Character.class, (char) 55, -1); } @Test void testShortConversions() { runTest("55", Short.class, (short) 55, -1); runTest(55, Short.class, (short) 55, -1); runTest(55.0, Short.class, (short) 55, -1); runTest(true, Short.class, (short) 1, -1); runTest((byte) 55, Short.class, (short) 55, -1); runTest((char) 55, Short.class, (short) 55, -1); runTest((short) 55, Short.class, (short) 55, -1); runTest(55L, Short.class, (short) 55, -1); runTest(55.0f, Short.class, (short) 55, -1); runTest(new BigInteger("55"), Short.class, (short) 55, -1); runTest(new BigDecimal("55"), Short.class, (short) 55, -1); } @Test void testLongConversions() { runTest("55", Long.class, 55L, -1); runTest(55, Long.class, 55L, -1); runTest(55.0, Long.class, 55L, -1); runTest(true, Long.class, 1L, -1); runTest((byte) 55, Long.class, 55L, -1); runTest((char) 55, Long.class, 55L, -1); runTest((short) 55, Long.class, 55L, -1); runTest(55L, Long.class, 55L, -1); runTest(55.0f, Long.class, 55L, -1); runTest(new BigInteger("55"), Long.class, 55L, -1); runTest(new BigDecimal("55"), Long.class, 55L, -1); } @Test void testFloatConversions() { runTest("55.1234", Float.class, 55.1234f, -1); runTest(55, Float.class, 55.0f, -1); runTest(55.1234, Float.class, 55.1234f, -1); runTest(true, Float.class, 1.0f, -1); runTest((byte) 55, Float.class, 55.0f, -1); runTest((char) 55, Float.class, 55.0f, -1); runTest((short) 55, Float.class, 55.0f, -1); runTest(55L, Float.class, 55.0f, -1); runTest(55.1234f, Float.class, 55.1234f, 4); runTest(new BigInteger("55"), Float.class, 55.0f, -1); runTest(new BigDecimal("55.1234"), Float.class, 55.1234f, -1); } @Test void testBigIntegerConversions() { runTest("55", BigInteger.class, new BigInteger("55"), -1); runTest(55, BigInteger.class, new BigInteger("55"), -1); runTest(55.0, BigInteger.class, new BigInteger("55"), -1); runTest(true, BigInteger.class, new BigInteger("1"), -1); runTest((byte) 55, BigInteger.class, new BigInteger("55"), -1); runTest((char) 55, BigInteger.class, new BigInteger("55"), -1); runTest((short) 55, BigInteger.class, new BigInteger("55"), -1); runTest(55L, BigInteger.class, new BigInteger("55"), -1); runTest(55.0f, BigInteger.class, new BigInteger("55"), -1); runTest(new BigInteger("55"), BigInteger.class, new BigInteger("55"), -1); runTest(new BigDecimal("55"), BigInteger.class, new BigInteger("55"), -1); } @Test void testBigDecimalConversions() { runTest("55.1234", BigDecimal.class, new BigDecimal("55.1234"), -1); runTest(55, BigDecimal.class, new BigDecimal("55"), -1); runTest(55.1234, BigDecimal.class, new BigDecimal("55.1234"), 4); runTest(true, BigDecimal.class, new BigDecimal("1"), -1); runTest((byte) 55, BigDecimal.class, new BigDecimal("55"), -1); runTest((char) 55, BigDecimal.class, new BigDecimal("55"), -1); runTest((short) 55, BigDecimal.class, new BigDecimal("55"), -1); runTest(55L, BigDecimal.class, new BigDecimal("55"), -1); runTest(55.1234f, BigDecimal.class, new BigDecimal("55.1234"), 4); runTest(new BigInteger("55"), BigDecimal.class, new BigDecimal("55"), -1); runTest(new BigDecimal("55.1234"), BigDecimal.class, new BigDecimal("55.1234"), -1); } } ================================================ FILE: ognl/src/test/java/ognl/test/ObjectIndexedPropertyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.Bean1; import ognl.test.objects.ObjectIndexed; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; class ObjectIndexedPropertyTest { private ObjectIndexed objectIndexed; private OgnlContext context; @BeforeEach void setUp() { objectIndexed = new ObjectIndexed(); context = Ognl.createDefaultContext(objectIndexed); } @Test void testGetNonIndexedPropertyThroughAttributesMap() throws OgnlException { Object actual = Ognl.getValue("attributes[\"bar\"]", context, objectIndexed); assertEquals("baz", actual); } @Test void testGetIndexedProperty() throws OgnlException { Object actual = Ognl.getValue("attribute[\"foo\"]", context, objectIndexed); assertEquals("bar", actual); } @Test void testSetIndexedProperty() throws OgnlException { Ognl.setValue("attribute[\"bar\"]", context, objectIndexed, "newValue"); Object actual = Ognl.getValue("attribute[\"bar\"]", context, objectIndexed); assertEquals("newValue", actual); } @Test void testGetPropertyBackThroughMapToConfirm() throws OgnlException { Ognl.setValue("attribute[\"bar\"]", context, objectIndexed, "newValue"); Object actual = Ognl.getValue("attributes[\"bar\"]", context, objectIndexed); assertEquals("newValue", actual); } @Test void testGetIndexedPropertyFromIndexedThenThroughOther() throws OgnlException { Object actual = Ognl.getValue("attribute[\"other\"].attribute[\"bar\"]", context, objectIndexed); assertEquals("baz", actual); } @Test void testGetPropertyBackThroughMapToConfirmFromIndexed() throws OgnlException { Object actual = Ognl.getValue("attribute[\"other\"].attributes[\"bar\"]", context, objectIndexed); assertEquals("baz", actual); } @Test void testIllegalDynamicSubscriptAccessToObjectIndexedProperty() { assertThrows(OgnlException.class, () -> Ognl.getValue("attribute[$]", context, objectIndexed)); } @Test void testBeanIndexedValue() throws OgnlException { Bean1 root = new Bean1(); Object actual = Ognl.getValue("bean2.bean3.indexedValue[25]", context, root); assertNull(actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/ObjectIndexedTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.SimpleNode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class ObjectIndexedTest { protected OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); } @Test void testObjectIndexAccess() throws OgnlException { SimpleNode expression = (SimpleNode) Ognl.parseExpression("#ka.sunk[#root]"); context.put("ka", new Test1()); Object actual = Ognl.getValue(expression, context, "aksdj"); assertEquals("foo", actual); } @Test void testObjectIndexInSubclass() throws OgnlException { SimpleNode expression = (SimpleNode) Ognl.parseExpression("#ka.sunk[#root]"); context.put("ka", new Test2()); Object actual = Ognl.getValue(expression, context, "aksdj"); assertEquals("foo", actual); } @Test void testMultipleObjectIndexGetters() throws OgnlException { SimpleNode expression = (SimpleNode) Ognl.parseExpression("#ka.sunk[#root]"); context.put("ka", new Test3()); assertThrows(OgnlException.class, () -> Ognl.getValue(expression, context, new Test3())); } @Test void testMultipleObjectIndexSetters() throws OgnlException { SimpleNode expression = (SimpleNode) Ognl.parseExpression("#ka.sunk[#root]"); context.put("ka", new Test4()); assertThrows(OgnlException.class, () -> Ognl.getValue(expression, context, "aksdj")); } @Test void testMultipleObjectIndexMethodPairs() throws OgnlException { SimpleNode expression = (SimpleNode) Ognl.parseExpression("#ka.sunk[#root]"); context.put("ka", new Test5()); assertThrows(OgnlException.class, () -> Ognl.getValue(expression, context, "aksdj")); } interface TestInterface { String getSunk(String index); void setSunk(String index, String sunk); } static class Test1 implements TestInterface { public String getSunk(String index) { return "foo"; } public void setSunk(String index, String sunk) { /* do nothing */ } } static class Test2 extends Test1 { public String getSunk(String index) { return "foo"; } public void setSunk(String index, String sunk) { /* do nothing */ } } static class Test3 extends Test1 { public String getSunk(String index) { return "foo"; } public void setSunk(String index, String sunk) { /* do nothing */ } public String getSunk(Object index) { return null; } } static class Test4 extends Test1 { public String getSunk(String index) { return "foo"; } public void setSunk(String index, String sunk) { /* do nothing */ } public void setSunk(Object index, String sunk) { /* do nothing */ } } static class Test5 extends Test1 { public String getSunk(String index) { return "foo"; } public void setSunk(String index, String sunk) { /* do nothing */ } public String getSunk(Object index) { return null; } public void setSunk(Object index, String sunk) { /* do nothing */ } } } ================================================ FILE: ognl/src/test/java/ognl/test/OgnlContextCreateTest.java ================================================ /* * Copyright 2020 OGNL Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ognl.test; import ognl.DefaultClassResolver; import ognl.DefaultTypeConverter; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.Simple; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class OgnlContextCreateTest> { @Test void createContext() throws OgnlException { C context = Ognl.createDefaultContext(null).withValues(prepareValues()); assertEquals("test100", Ognl.getValue("#test", context, new Simple())); } @Test void createContextWithRoot() throws OgnlException { Simple root = new Simple(); C context = Ognl.createDefaultContext(root, prepareValues()); assertEquals("test100", Ognl.getValue("#test", context, root)); } @Test void createContextWithNullRoot() throws OgnlException { Simple root = new Simple(); C context = Ognl.createDefaultContext(null, prepareValues()); assertEquals("test100", Ognl.getValue("#test", context, root)); } @Test void createContextWithClassResolver() throws OgnlException { Simple root = new Simple(); OgnlContext context = Ognl.createDefaultContext(root, new MyClassResolver()); assertEquals("static", Ognl.getValue("@ognl.test.MyClass@getValue()", context, root)); } @Test void addContextWithClassResolver() throws OgnlException { Simple root = new Simple(); OgnlContext oldContext = Ognl.createDefaultContext(root, new MyClassResolver()); OgnlContext context = Ognl.addDefaultContext(root, oldContext.getMemberAccess(), oldContext.getClassResolver(), oldContext.getTypeConverter()); assertEquals("static", Ognl.getValue("@ognl.test.MyClass@getValue()", context, root)); } @Test void createContextWithNullRootAndClassResolver() throws OgnlException { Simple root = new Simple(); OgnlContext context = Ognl.createDefaultContext(null, new MyClassResolver()); assertEquals("static", Ognl.getValue("@ognl.test.MyClass@getValue()", context, root)); } @Test void addContextWithNullRootAndClassResolver() throws OgnlException { Simple root = new Simple(); OgnlContext oldContext = Ognl.createDefaultContext(null, new MyClassResolver()); OgnlContext context = Ognl.addDefaultContext(null, oldContext.getMemberAccess(), oldContext.getClassResolver(), oldContext.getTypeConverter()); assertEquals("static", Ognl.getValue("@ognl.test.MyClass@getValue()", context, root)); } @Test void createContextWithClassResolverAndTypeConverter() throws OgnlException { Simple root = new Simple(); OgnlContext context = Ognl.createDefaultContext(root, new MyClassResolver(), new MyTypeConverter()); Simple actual = (Simple) Ognl.getValue("@ognl.test.MyClass@getValue()", context, root, Simple.class); assertNotNull(actual); assertArrayEquals(new Object[]{"static"}, actual.getValues()); } @Test void addContextWithClassResolverAndTypeConverter() throws OgnlException { Simple root = new Simple(); OgnlContext oldContext = Ognl.createDefaultContext(root, new MyClassResolver(), new MyTypeConverter()); OgnlContext context = Ognl.addDefaultContext(null, oldContext.getMemberAccess(), oldContext.getClassResolver(), oldContext.getTypeConverter()); Simple actual = (Simple) Ognl.getValue("@ognl.test.MyClass@getValue()", context, root, Simple.class); assertNotNull(actual); assertArrayEquals(new Object[]{"static"}, actual.getValues()); } @Test void addContextWithClassResolverAndNoTypeConverter() throws OgnlException { Simple root = new Simple(); OgnlContext oldContext = Ognl.createDefaultContext(root, null, new MyTypeConverter()); OgnlContext context = Ognl.addDefaultContext(null, oldContext.getMemberAccess(), new MyClassResolver(), oldContext.getTypeConverter()); Simple actual = (Simple) Ognl.getValue("@ognl.test.MyClass@getValue()", context, root, Simple.class); assertNotNull(actual); assertArrayEquals(new Object[]{"static"}, actual.getValues()); } @Test void addContextWithNoClassResolverAndNoTypeConverter() throws OgnlException { Simple root = new Simple(); C oldContext = Ognl.createDefaultContext(root); C context = Ognl.addDefaultContext(null, new MyClassResolver<>(), new MyTypeConverter<>(), oldContext); Simple actual = (Simple) Ognl.getValue("@ognl.test.MyClass@getValue()", context, root, Simple.class); assertNotNull(actual); assertArrayEquals(new Object[]{"static"}, actual.getValues()); } @Test void createContextWithNullRootAndClassResolverAndTypeConverter() throws OgnlException { Simple root = new Simple(); OgnlContext context = Ognl.createDefaultContext(null, new MyClassResolver(), new MyTypeConverter()); Simple actual = (Simple) Ognl.getValue("@ognl.test.MyClass@getValue()", context, root, Simple.class); assertNotNull(actual); assertArrayEquals(new Object[]{"static"}, actual.getValues()); } @Test void addContextWithNullRootAndClassResolverAndTypeConverter() throws OgnlException { Simple root = new Simple(); OgnlContext oldContext = Ognl.createDefaultContext(null, new MyClassResolver(), new MyTypeConverter()); OgnlContext context = Ognl.addDefaultContext(null, oldContext.getMemberAccess(), new MyClassResolver(), oldContext.getTypeConverter(), oldContext); Simple actual = (Simple) Ognl.getValue("@ognl.test.MyClass@getValue()", context, root, Simple.class); assertNotNull(actual); assertArrayEquals(new Object[]{"static"}, actual.getValues()); } private Map prepareValues() { Map values = new HashMap<>(); values.put("test", "test100"); return values; } private static class MyClassResolver> extends DefaultClassResolver { @Override public Class classForName(String className, C context) throws ClassNotFoundException { if (className.equals("ognl.test.MyClass")) { return (Class) MyClass.class; } return super.classForName(className, context); } } private static class MyClass { public static String getValue() { return "static"; } } private static class MyTypeConverter> extends DefaultTypeConverter { @Override public Object convertValue(C context, Object value, Class toType) { if (toType == Simple.class) { return new Simple(new Object[]{value}); } return super.convertValue(context, value, toType); } } } ================================================ FILE: ognl/src/test/java/ognl/test/OgnlExceptionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.OgnlException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; /** * Tests {@link OgnlException}. */ class OgnlExceptionTest { @Test void test_Throwable_Reason() { try { throwException(); } catch (OgnlException e) { assertInstanceOf(NumberFormatException.class, e.getReason()); assertEquals("Unable to parse input string.", e.getMessage()); } } void throwException() throws OgnlException { try { Integer.parseInt("45ac"); } catch (NumberFormatException et) { throw new OgnlException("Unable to parse input string.", et); } } } ================================================ FILE: ognl/src/test/java/ognl/test/OgnlOpsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.OgnlOps; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.math.BigInteger; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class OgnlOpsTest { @Test void testEqualStringsEqual() { final String v1 = "a"; final String v2 = "a"; final boolean res = OgnlOps.equal(v1, v2); assertTrue(res); } @Test void testEqualStringsNotEqual() { final String v1 = "a"; final String v2 = "b"; final boolean res = OgnlOps.equal(v1, v2); assertFalse(res); } @Test void testEqualFloatsEqual() { final Float v1 = 0.1f; final Float v2 = 0.1f; final boolean res = OgnlOps.equal(v1, v2); assertTrue(res); } @Test void testEqualFloatsNotEqual() { final Float v1 = 0.1f; final Float v2 = 0.2f; final boolean res = OgnlOps.equal(v1, v2); assertFalse(res); } @Test void testEqualLongsEqual() { final Long v1 = 1L; final Long v2 = 1L; final boolean res = OgnlOps.equal(v1, v2); assertTrue(res); } @Test void testEqualLongsNotEqual() { final Long v1 = 1L; final Long v2 = 2L; final boolean res = OgnlOps.equal(v1, v2); assertFalse(res); } @Test void testEqualBigLongsEqual() { final Long v1 = 1000000000000000001L; final Long v2 = 1000000000000000001L; final boolean res = OgnlOps.equal(v1, v2); assertTrue(res); } @Test void testEqualBigLongsNotEqual() { final Long v1 = 1000000000000000001L; final Long v2 = 1000000000000000002L; final boolean res = OgnlOps.equal(v1, v2); assertFalse(res); } @Test void testEqualNullsEqual() { assertTrue(OgnlOps.equal(null, null)); } @Test void testEqualNullsNotEqual() { final Object v2 = "b"; assertFalse(OgnlOps.equal(null, v2)); assertFalse(OgnlOps.equal(v2, null)); } @Test void testShiftLeft() { assertEquals(8, OgnlOps.shiftLeft(1, 3)); assertEquals(new BigInteger("8"), OgnlOps.shiftLeft(new BigInteger("1"), 3)); } @Test void testShiftRight() { assertEquals(1, OgnlOps.shiftRight(8, 3)); assertEquals(new BigInteger("1"), OgnlOps.shiftRight(new BigInteger("8"), 3)); } @Test void testUnsignedShiftRight() { assertEquals(1, OgnlOps.unsignedShiftRight(8, 3)); assertEquals(new BigInteger("1"), OgnlOps.unsignedShiftRight(new BigInteger("8"), 3)); } @Test void testAdd() { assertEquals(5, OgnlOps.add(2, 3)); assertEquals(new BigInteger("5"), OgnlOps.add(new BigInteger("2"), new BigInteger("3"))); assertEquals(new BigDecimal("5.0"), OgnlOps.add(new BigDecimal("2.0"), new BigDecimal("3.0"))); } @Test void testSubtract() { assertEquals(1, OgnlOps.subtract(3, 2)); assertEquals(new BigInteger("1"), OgnlOps.subtract(new BigInteger("3"), new BigInteger("2"))); assertEquals(new BigDecimal("1.0"), OgnlOps.subtract(new BigDecimal("3.0"), new BigDecimal("2.0"))); } @Test void testMultiply() { assertEquals(6, OgnlOps.multiply(2, 3)); assertEquals(new BigInteger("6"), OgnlOps.multiply(new BigInteger("2"), new BigInteger("3"))); assertEquals(new BigDecimal("6.00"), OgnlOps.multiply(new BigDecimal("2.0"), new BigDecimal("3.0"))); } @Test void testDivide() { assertEquals(2, OgnlOps.divide(6, 3)); assertEquals(new BigInteger("2"), OgnlOps.divide(new BigInteger("6"), new BigInteger("3"))); assertEquals(new BigDecimal("2.0"), OgnlOps.divide(new BigDecimal("6.0"), new BigDecimal("3.0"))); } @Test void testRemainder() { assertEquals(1, OgnlOps.remainder(7, 3)); assertEquals(new BigInteger("1"), OgnlOps.remainder(new BigInteger("7"), new BigInteger("3"))); } @Test void testNegate() { assertEquals(-1, OgnlOps.negate(1)); assertEquals(new BigInteger("-1"), OgnlOps.negate(new BigInteger("1"))); assertEquals(new BigDecimal("-1.0"), OgnlOps.negate(new BigDecimal("1.0"))); } @Test void testBitNegate() { assertEquals(~1, OgnlOps.bitNegate(1)); assertEquals(new BigInteger("-2"), OgnlOps.bitNegate(new BigInteger("1"))); } @Test void testGetEscapeString() { assertEquals("\\t", OgnlOps.getEscapeString("\t")); assertEquals("\\n", OgnlOps.getEscapeString("\n")); } @Test void testGetEscapedChar() { assertEquals("\\t", OgnlOps.getEscapedChar('\t')); assertEquals("\\n", OgnlOps.getEscapedChar('\n')); } @Test void testReturnValue() { assertEquals("test", OgnlOps.returnValue(null, "test")); } @Test void testCastToRuntime() { RuntimeException ex = new RuntimeException("test"); assertEquals(ex, OgnlOps.castToRuntime(ex)); } } ================================================ FILE: ognl/src/test/java/ognl/test/OperationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.SimpleNode; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests for {@link SimpleNode#isOperation(OgnlContext)}. */ class OperationTest { @Test void test_isOperation() throws Exception { OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); SimpleNode node = (SimpleNode) Ognl.parseExpression("#name"); assertFalse(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("#name = 'boo'"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("#name['foo'] = 'bar'"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("#name.foo = 'bar' + 'foo'"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("{name.foo = 'bar' + 'foo', #name.foo()}"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("('bar' + 'foo', #name.foo())"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("-bar"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("-(#bar)"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("-1"); assertFalse(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("-(#bar+#foo)"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("#bar=3,#foo=4(#bar-#foo)"); assertTrue(node.isOperation(context)); node = (SimpleNode) Ognl.parseExpression("#bar-3"); assertTrue(node.isOperation(context)); } } ================================================ FILE: ognl/src/test/java/ognl/test/OperatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.SimpleNode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class OperatorTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } @Test void testStringComparisons() throws Exception { assertExpression("\"one\" > \"two\"", Boolean.FALSE); assertExpression("\"one\" >= \"two\"", Boolean.FALSE); assertExpression("\"one\" < \"two\"", Boolean.TRUE); assertExpression("\"one\" <= \"two\"", Boolean.TRUE); assertExpression("\"one\" == \"two\"", Boolean.FALSE); assertExpression("\"o\" > \"o\"", Boolean.FALSE); assertExpression("\"o\" gt \"o\"", Boolean.FALSE); assertExpression("\"o\" >= \"o\"", Boolean.TRUE); assertExpression("\"o\" gte \"o\"", Boolean.TRUE); assertExpression("\"o\" < \"o\"", Boolean.FALSE); assertExpression("\"o\" lt \"o\"", Boolean.FALSE); assertExpression("\"o\" <= \"o\"", Boolean.TRUE); assertExpression("\"o\" lte \"o\"", Boolean.TRUE); assertExpression("\"o\" == \"o\"", Boolean.TRUE); assertExpression("\"o\" eq \"o\"", Boolean.TRUE); } private void assertExpression(String expressionString, Object expectedResult) throws Exception { SimpleNode expression = (SimpleNode) Ognl.parseExpression(expressionString); Object result = Ognl.getValue(expression, context, (Object) null); assertEquals(expectedResult, result); } } ================================================ FILE: ognl/src/test/java/ognl/test/PackageKeywordTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; /** * Tests for Issue #103: Class Reference Parser Fails with "or" in Package Names * This test verifies that OGNL can parse class references containing reserved keywords * like "or", "and", "not", etc. in package names. */ class PackageKeywordTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root, new DefaultMemberAccess(true)); } /** * Baseline test: java.util.UUID can be referenced (no keywords in package) */ @Test void javaUtilUUID() throws Exception { Object result = Ognl.getValue("@java.util.UUID@randomUUID()", context, root); assertNotNull(result); assertEquals(UUID.class, result.getClass()); } /** * Verify parsing works with actual Java class containing no keywords */ @Test void classReferenceWithAtomicInteger() throws Exception { Object result = Ognl.getValue("@java.util.concurrent.atomic.AtomicInteger@class", context, root); assertEquals(java.util.concurrent.atomic.AtomicInteger.class, result); } /** * Issue #103: Parse expression with "or" keyword in package name. * Before fix: ExpressionSyntaxException at parse time * After fix: Should parse successfully */ @Test void parseExpressionWithOrInPackage() throws Exception { try { Ognl.parseExpression("@jp.or.example.IdUtils@generateId()"); } catch (ognl.ExpressionSyntaxException e) { fail("Parser should accept 'or' as part of package name: " + e.getMessage()); } } /** * Verify "and" keyword is accepted in package names */ @Test void parseExpressionWithAndInPackage() throws Exception { try { Ognl.parseExpression("@com.and.example.Utils@method()"); } catch (ognl.ExpressionSyntaxException e) { fail("Parser should accept 'and' as part of package name: " + e.getMessage()); } } /** * Verify "not" keyword is accepted in package names */ @Test void parseExpressionWithNotInPackage() throws Exception { try { Ognl.parseExpression("@org.not.example.Utils@method()"); } catch (ognl.ExpressionSyntaxException e) { fail("Parser should accept 'not' as part of package name: " + e.getMessage()); } } /** * Verify "in" keyword is accepted in package names */ @Test void parseExpressionWithInInPackage() throws Exception { try { Ognl.parseExpression("@org.example.in.Utils@method()"); } catch (ognl.ExpressionSyntaxException e) { fail("Parser should accept 'in' as part of package name: " + e.getMessage()); } } /** * Verify multiple keywords can be combined in package names */ @Test void parseExpressionWithMultipleKeywordsInPackage() throws Exception { try { Ognl.parseExpression("@org.not.and.or.Utils@field"); } catch (ognl.ExpressionSyntaxException e) { fail("Parser should accept multiple keywords in package name: " + e.getMessage()); } } } ================================================ FILE: ognl/src/test/java/ognl/test/PrimitiveArrayTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; class PrimitiveArrayTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root); } @Test void testBooleanArrayCreation() throws Exception { boolean[] actual = (boolean[]) Ognl.getValue("new boolean[5]", context, root); assertArrayEquals(new boolean[5], actual); actual = (boolean[]) Ognl.getValue("new boolean[] { true, false }", context, root); assertArrayEquals(new boolean[]{true, false}, actual); actual = (boolean[]) Ognl.getValue("new boolean[] { 0, 1, 5.5 }", context, root); assertArrayEquals(new boolean[]{false, true, true}, actual); } @Test void testCharArrayCreation() throws Exception { char[] actual = (char[]) Ognl.getValue("new char[] { 'a', 'b' }", context, root); assertArrayEquals(new char[]{'a', 'b'}, actual); actual = (char[]) Ognl.getValue("new char[] { 10, 11 }", context, root); assertArrayEquals(new char[]{(char) 10, (char) 11}, actual); } @Test void testByteArrayCreation() throws Exception { byte[] actual = (byte[]) Ognl.getValue("new byte[] { 1, 2 }", context, root); assertArrayEquals(new byte[]{1, 2}, actual); } @Test void testShortArrayCreation() throws Exception { short[] actual = (short[]) Ognl.getValue("new short[] { 1, 2 }", context, root); assertArrayEquals(new short[]{1, 2}, actual); } @Test void testIntArrayCreation() throws Exception { int[] actual = (int[]) Ognl.getValue("new int[six]", context, root); assertArrayEquals(new int[root.six], actual); actual = (int[]) Ognl.getValue("new int[#root.six]", context, root); assertArrayEquals(new int[root.six], actual); actual = (int[]) Ognl.getValue("new int[6]", context, root); assertArrayEquals(new int[6], actual); actual = (int[]) Ognl.getValue("new int[] { 1, 2 }", context, root); assertArrayEquals(new int[]{1, 2}, actual); } @Test void testLongArrayCreation() throws Exception { long[] actual = (long[]) Ognl.getValue("new long[] { 1, 2 }", context, root); assertArrayEquals(new long[]{1, 2}, actual); } @Test void testFloatArrayCreation() throws Exception { float[] actual = (float[]) Ognl.getValue("new float[] { 1, 2 }", context, root); assertArrayEquals(new float[]{1, 2}, actual); } @Test void testDoubleArrayCreation() throws Exception { double[] actual = (double[]) Ognl.getValue("new double[] { 1, 2 }", context, root); assertArrayEquals(new double[]{1, 2}, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/PrimitiveNullHandlingTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PrimitiveNullHandlingTest { private Simple simple; private OgnlContext context; @BeforeEach void setUp() { simple = new Simple(); simple.setFloatValue(10.56f); simple.setIntValue(34); context = Ognl.createDefaultContext(simple); } @Test void testFloatValue() throws Exception { Object actual = Ognl.getValue("floatValue", context, simple); assertEquals(10.56f, actual); Ognl.setValue("floatValue", context, simple, null); actual = Ognl.getValue("floatValue", context, simple); assertEquals(0f, actual); } @Test void testIntValue() throws Exception { Object actual = Ognl.getValue("intValue", context, simple); assertEquals(34, actual); Ognl.setValue("intValue", context, simple, null); actual = Ognl.getValue("intValue", context, simple); assertEquals(0, actual); } @Test void testBooleanValue() throws Exception { Object actual = Ognl.getValue("booleanValue", context, simple); assertEquals(false, actual); Ognl.setValue("booleanValue", context, simple, true); actual = Ognl.getValue("booleanValue", context, simple); assertEquals(true, actual); Ognl.setValue("booleanValue", context, simple, null); actual = Ognl.getValue("booleanValue", context, simple); assertEquals(false, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/PrivateAccessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PrivateAccessorTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root, new DefaultMemberAccess(true)); } @Test void testPrivateAccessorIntValue() throws Exception { Object actual = Ognl.getValue("getPrivateAccessorIntValue()", context, root); assertEquals(67, actual); actual = Ognl.getValue("privateAccessorIntValue", context, root); assertEquals(67, actual); Ognl.setValue("privateAccessorIntValue", context, root, 100); actual = Ognl.getValue("privateAccessorIntValue", context, root); assertEquals(100, actual); } @Test void testPrivateAccessorIntValue2() throws Exception { Object actual = Ognl.getValue("privateAccessorIntValue2", context, root); assertEquals(67, actual); Ognl.setValue("privateAccessorIntValue2", context, root, 100); actual = Ognl.getValue("privateAccessorIntValue2", context, root); assertEquals(100, actual); } @Test void testPrivateAccessorIntValue3() throws Exception { Object actual = Ognl.getValue("privateAccessorIntValue3", context, root); assertEquals(67, actual); Ognl.setValue("privateAccessorIntValue3", context, root, 100); actual = Ognl.getValue("privateAccessorIntValue3", context, root); assertEquals(100, actual); } @Test void testPrivateAccessorBooleanValue() throws Exception { Object actual = Ognl.getValue("privateAccessorBooleanValue", context, root); assertEquals(true, actual); Ognl.setValue("privateAccessorBooleanValue", context, root, false); actual = Ognl.getValue("privateAccessorBooleanValue", context, root); assertEquals(false, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/PrivateMemberTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; class PrivateMemberTest { private static final String _privateStaticProperty = "private static value"; private String _privateProperty = "private value"; private final String _privateFinalProperty = "private final value"; private static final String _privateStaticFinalProperty = "private static final value"; private OgnlContext context; private String getPrivateProperty() { return _privateProperty; } private static String getPrivateStaticProperty() { return _privateStaticProperty; } private String getPrivateFinalProperty() { return _privateFinalProperty; } private static String getPrivateStaticFinalProperty() { return _privateStaticFinalProperty; } @BeforeEach void setUp() { context = Ognl.createDefaultContext(this, new DefaultMemberAccess(true, false, false)); } @Test void testPrivateAccessor() throws OgnlException { Object actual = Ognl.getValue("privateProperty", context, this); assertEquals(getPrivateProperty(), actual); } @Test void testPrivateField() throws OgnlException { Object actual = Ognl.getValue("_privateProperty", context, this); assertEquals(_privateProperty, actual); } @Test void testPrivateFinalAccessor() throws OgnlException { Object actual = Ognl.getValue("privateFinalProperty", context, this); assertEquals(getPrivateFinalProperty(), actual); } @Test void testPrivateFinalField() throws OgnlException { Object actual = Ognl.getValue("_privateFinalProperty", context, this); assertEquals(_privateFinalProperty, actual); } @Test void testPrivateStaticAccessor() throws OgnlException { Object actual = Ognl.getValue("privateStaticProperty", context, this); assertEquals(getPrivateStaticProperty(), actual); } @Test void testPrivateStaticFieldNormalAccess() { try { Object actual = Ognl.getValue("_privateStaticProperty", context, this); assertEquals(_privateStaticProperty, actual); fail("Should not be able to access private static _privateStaticProperty through getValue()"); } catch (OgnlException oex) { assertEquals("_privateStaticProperty", oex.getMessage()); } } @Test void testPrivateStaticFieldStaticAccess() throws OgnlException { Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_privateStaticProperty"); assertEquals(_privateStaticProperty, actual); } @Test void testPrivateStaticFinalAccessor() throws OgnlException { Object actual = Ognl.getValue("privateStaticFinalProperty", context, this); assertEquals(actual, getPrivateStaticFinalProperty()); } @Test void testPrivateStaticFinalFieldNormalAccess() { try { Object actual = Ognl.getValue("_privateStaticFinalProperty", context, this); assertEquals(_privateStaticFinalProperty, actual); fail("Should not be able to access private static _privateStaticFinalProperty through getValue()"); } catch (OgnlException oex) { assertEquals("_privateStaticFinalProperty", oex.getMessage()); } } @Test void testPrivateStaticFinalFieldStaticAccess() throws OgnlException { Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_privateStaticFinalProperty"); assertEquals(_privateStaticFinalProperty, actual); } @Test void testPrivateFieldSet() throws OgnlException { final String originalValue = _privateProperty; Object actual = Ognl.getValue("_privateProperty", context, this); assertEquals(originalValue, actual); Ognl.setValue("_privateProperty", context, this, "changevalue"); actual = Ognl.getValue("_privateProperty", context, this); assertEquals("changevalue", actual); Ognl.setValue("_privateProperty", context, this, originalValue); actual = Ognl.getValue("_privateProperty", context, this); assertEquals(actual, originalValue); } @Test void testPrivateFinalFieldSet() throws OgnlException { final String originalValue = _privateFinalProperty; Object actual = Ognl.getValue("_privateFinalProperty", context, this); assertEquals(originalValue, actual); try { Ognl.setValue("_privateFinalProperty", context, this, "changevalue"); fail("Should not be able to modify final property"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateFinalProperty", oex.getMessage()); } actual = Ognl.getValue("_privateFinalProperty", context, this); assertEquals(originalValue, actual); } @Test void testPrivateStaticFieldSet() throws OgnlException { final String originalValue = _privateStaticProperty; Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_privateStaticProperty"); assertEquals(originalValue, actual); try { Ognl.setValue("_privateStaticProperty", context, this, "changevalue"); fail("Should not be able to modify static property"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateStaticProperty", oex.getMessage()); } actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_privateStaticProperty"); assertEquals(originalValue, actual); } @Test void testPrivateStaticFinalFieldSet() throws OgnlException { final String originalValue = _privateStaticFinalProperty; Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_privateStaticFinalProperty"); assertEquals(originalValue, actual); try { Ognl.setValue("_privateStaticFinalProperty", context, this, "changevalue"); fail("Should not be able to modify static property"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateStaticFinalProperty", oex.getMessage()); } actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_privateStaticFinalProperty"); assertEquals(originalValue, actual); } @Test void testPrivateFieldSetFail() { context = Ognl.createDefaultContext(this, new DefaultMemberAccess(false, true, true), null, null); try { Ognl.setValue("_privateProperty", context, this, "changevalue"); fail("Should not be able to set private property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateProperty", oex.getMessage()); } } @Test void testPrivateFinalFieldSetFail() { context = Ognl.createDefaultContext(this, new DefaultMemberAccess(false, true, true), null, null); try { Ognl.setValue("_privateFinalProperty", context, this, "changevalue"); fail("Should not be able to set private property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateFinalProperty", oex.getMessage()); } } @Test void testPrivateStaticFieldSetFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Ognl.setValue("_privateStaticProperty", context, this, "changevalue"); fail("Should not be able to set private property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateStaticProperty", oex.getMessage()); } } @Test void testPrivateStaticFinalFieldSetFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Ognl.setValue("_privateStaticFinalProperty", context, this, "changevalue"); fail("Should not be able to set private property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateStaticFinalProperty", oex.getMessage()); } } @Test void testPrivateAccessorFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = Ognl.getValue("privateProperty", context, this); assertEquals(actual, getPrivateProperty()); fail("Should not be able to access private property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest.privateProperty", oex.getMessage()); } } @Test void testPrivateFieldFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = Ognl.getValue("_privateProperty", context, this); assertEquals(actual, _privateProperty); fail("Should not be able to access private property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateProperty", oex.getMessage()); } } @Test void testPrivateFinalAccessorFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = Ognl.getValue("privateFinalProperty", context, this); assertEquals(actual, getPrivateFinalProperty()); fail("Should not be able to access private final property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest.privateFinalProperty", oex.getMessage()); } } @Test void testPrivateFinalFieldFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = Ognl.getValue("_privateFinalProperty", context, this); assertEquals(actual, _privateFinalProperty); fail("Should not be able to access private final property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateFinalProperty", oex.getMessage()); } } @Test void testPrivateStaticAccessorFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = Ognl.getValue("privateStaticProperty", context, this); assertEquals(actual, getPrivateStaticProperty()); fail("Should not be able to access private static property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest.privateStaticProperty", oex.getMessage()); } } @Test void testPrivateStaticFieldNormalAccessFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = Ognl.getValue("_privateStaticProperty", context, this); assertEquals(_privateStaticProperty, actual); fail("Should not be able to access private static property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateStaticProperty", oex.getMessage()); } } @Test void testPrivateStaticFieldStaticAccessFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_privateStaticProperty"); assertEquals(_privateStaticProperty, actual); fail("Should not be able to access private static property with private access turned off"); } catch (OgnlException oex) { assertEquals("Could not get static field _privateStaticProperty from class ognl.test.PrivateMemberTest", oex.getMessage()); } } @Test void testPrivateStaticFinalAccessorFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = Ognl.getValue("privateStaticFinalProperty", context, this); assertEquals(actual, getPrivateStaticFinalProperty()); fail("Should not be able to access private static final property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest.privateStaticFinalProperty", oex.getMessage()); } } @Test void testPrivateStaticFinalFieldNormalAccessFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = Ognl.getValue("_privateStaticFinalProperty", context, this); assertEquals(_privateStaticFinalProperty, actual); fail("Should not be able to access private static final property with private access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.PrivateMemberTest._privateStaticFinalProperty", oex.getMessage()); } } @Test void testPrivateStaticFinalFieldStaticAccessFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, true)); try { Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_privateStaticFinalProperty"); assertEquals(_privateStaticFinalProperty, actual); fail("Should not be able to access private static final property with private access turned off"); } catch (OgnlException oex) { assertEquals("Could not get static field _privateStaticFinalProperty from class ognl.test.PrivateMemberTest", oex.getMessage()); } } } ================================================ FILE: ognl/src/test/java/ognl/test/ProjectionSelectionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigInteger; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; class ProjectionSelectionTest { private Root root; @BeforeEach void setUp() { root = new Root(); } @Test void testProjectionClass() throws Exception { Object actual = Ognl.getValue("array.{class}", root); List> expected = Arrays.asList(Integer.class, Integer.class, Integer.class, Integer.class); assertEquals(expected, actual); } @Test void testSelection() throws Exception { Object actual = Ognl.getValue("map.array.{? #this > 2 }", root); assertEquals(Arrays.asList(3, 4), actual); actual = Ognl.getValue("map.array.{^ #this > 2 }", root); assertEquals(List.of(3), actual); actual = Ognl.getValue("map.array.{$ #this > 2 }", root); assertEquals(List.of(4), actual); actual = Ognl.getValue("map.array[*].{?true} instanceof java.util.Collection", root); assertEquals(Boolean.TRUE, actual); } @Test void testFactorial() throws Exception { Object actual = Ognl.getValue("#fact=1, 30H.{? #fact = #fact * (#this+1), false }, #fact", root); assertEquals(new BigInteger("265252859812191058636308480000000"), actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/PropertyAccessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import ognl.PropertyAccessor; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PropertyAccessorTest> { private C context; @BeforeEach void setUp() { this.context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); OgnlRuntime.setPropertyAccessor(Parent.class, new ChildPropertyAccessor()); } @Test void shouldAccessProperty_usingCustomAccessor() throws Exception { // given Parent root = new Parent(new Child("Luk")); String expectedResult = "Luk"; // then assertEquals(expectedResult, Ognl.getValue("child", context, root)); } static class Child { String name; public Child(String name) { this.name = name; } public String getName() { return name; } } public static class Parent { Child child; public Parent(Child child) { this.child = child; } public Child getChild() { return child; } } public static class ChildPropertyAccessor> implements PropertyAccessor { public void setProperty(C context, Object target, Object name, Object value) throws OgnlException { } public Object getProperty(C context, Object target, Object name) throws OgnlException { if (target instanceof Parent && "child".equals(name)) { return OgnlRuntime.getProperty(context, ((Parent) target).getChild(), "name"); } return null; } public String getSourceAccessor(C context, Object target, Object index) { return index.toString(); } public String getSourceSetter(C context, Object target, Object index) { return index.toString(); } } } ================================================ FILE: ognl/src/test/java/ognl/test/PropertyArithmeticAndLogicalOperatorsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import ognl.test.objects.SimpleNumeric; import ognl.test.objects.TestModel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; class PropertyArithmeticAndLogicalOperatorsTest { private Root root; private TestModel model; private SimpleNumeric numeric; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); model = new TestModel(); numeric = new SimpleNumeric(); context = Ognl.createDefaultContext(root); } @Test void testBooleanExpressions() throws Exception { assertEquals(Boolean.TRUE, Ognl.getValue("objectIndex > 0", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("false", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("!false || true", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("property.bean3.value >= 24", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("(unassignedCopyModel.optionCount > 0 && canApproveCopy) || entry.copy.size() > 0", context, model)); assertEquals(Boolean.FALSE, Ognl.getValue(" !(printDelivery || @Boolean@FALSE)", context, root)); } @Test void testIntegerExpressions() throws Exception { assertEquals(1, Ognl.getValue("genericIndex-1", context, root)); assertEquals(((root.getRenderNavigation() ? 0 : 1) + root.getMap().size()) * root.getTheInt(), Ognl.getValue("((renderNavigation ? 0 : 1) + map.size) * theInt", context, root)); assertEquals(Arrays.asList(root.getTheInt() + 1), Ognl.getValue("{theInt + 1}", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("(getIndexedProperty('nested').size - 1) > genericIndex", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("(getIndexedProperty('nested').size + 1) >= genericIndex", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("(getIndexedProperty('nested').size + 1) == genericIndex", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("(getIndexedProperty('nested').size + 1) < genericIndex", context, root)); assertEquals(root.getMap().size() * ((Integer) root.getGenericIndex()).intValue(), Ognl.getValue("map.size * genericIndex", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("property == property", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("property.bean3.value % 2 == 0", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("genericIndex % 3 == 0", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("genericIndex % theInt == property.bean3.value", context, root)); assertEquals(root.getTheInt() / 100.0, Ognl.getValue("theInt / 100.0", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("@java.lang.Long@valueOf('100') == @java.lang.Long@valueOf('100')", context, root)); } @Test void testDoubleExpressions() throws Exception { assertEquals(numeric.getBudget() - numeric.getTimeBilled(), Ognl.getValue("budget - timeBilled", context, numeric)); assertEquals(Boolean.TRUE, Ognl.getValue("(budget % tableSize) == 0", context, numeric)); } } ================================================ FILE: ognl/src/test/java/ognl/test/PropertySetterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Node; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests being able to set property on object with interface that doesn't define setter. * See OGNL-115. */ class PropertySetterTest { private final TestObject testObject = new TestObject("propertyValue"); public interface TestInterface { String getProperty(); } public static class TestObject implements TestInterface { private String property; public TestObject(String property) { this.property = property; } public String getProperty() { return property; } public void setProperty(String property) { this.property = property; } public Integer getIntegerProperty() { return 1; } } public String getKey() { return "key"; } public TestObject getObject() { return testObject; } public TestInterface getInterfaceObject() { return testObject; } public String getPropertyKey() { return "property"; } @Test public void testEnhancedOgnl() throws Exception { OgnlContext context = Ognl.createDefaultContext(null); Node expression = Ognl.compileExpression(context, null, "interfaceObject.property"); Ognl.setValue(expression, context, this, "hello"); assertEquals("hello", getObject().getProperty()); // Fails if an interface is defined, but succeeds if not context.clear(); expression = Ognl.compileExpression(context, this.getObject(), "property"); Ognl.setValue(expression, context, this.getObject(), "hello"); assertEquals("hello", getObject().getProperty()); } } ================================================ FILE: ognl/src/test/java/ognl/test/PropertyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.BaseBean; import ognl.test.objects.Bean2; import ognl.test.objects.FirstBean; import ognl.test.objects.PropertyHolder; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.text.SimpleDateFormat; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class PropertyTest { private static final SimpleDateFormat DATETIME_FORMAT = new SimpleDateFormat("MM/dd/yyyy hh:mm a 'CST'"); /** * Used in tests */ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy"); private static final String VALUE = "foo"; private Root root; private BaseBean bean; private PropertyHolder property; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); bean = new FirstBean(); property = new PropertyHolder(); context = Ognl.createDefaultContext(root, new DefaultMemberAccess(true)); } @Test void testBooleanExpressions() throws Exception { assertEquals(Boolean.TRUE, Ognl.getValue("testString != null && !false", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("!getRenderNavigation() and !getReadonly()", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("!bean2.pageBreakAfter", context, root)); } @Test void testMapExpressions() throws Exception { assertEquals(root.getMap(), Ognl.getValue("map", context, root)); assertEquals(root, Ognl.getValue("map.test", context, root)); assertEquals(root, Ognl.getValue("map[\"test\"]", context, root)); assertEquals(root, Ognl.getValue("map[\"te\" + \"st\"]", context, root)); assertEquals(root.getMap().get(Root.SIZE_STRING), Ognl.getValue("map[(\"s\" + \"i\") + \"ze\"]", context, root)); assertEquals(root.getMap().get(Root.SIZE_STRING), Ognl.getValue("map[\"size\"]", context, root)); assertEquals(root.getMap().get(Root.SIZE_STRING), Ognl.getValue("map[@ognl.test.objects.Root@SIZE_STRING]", context, root)); } @Test void testStringExpressions() throws Exception { assertEquals(Boolean.FALSE, Ognl.getValue("stringValue != null && stringValue.length() > 0", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("indexedStringValue != null && indexedStringValue.length() > 0", context, root)); } @Test void testArrayExpressions() throws Exception { assertEquals(root.getList(), Ognl.getValue("map.list", context, root)); assertEquals(root.getArray()[0], Ognl.getValue("map.array[0]", context, root)); assertEquals(root.getList().get(1), Ognl.getValue("map.list[1]", context, root)); assertEquals(99, Ognl.getValue("map[^]", context, root)); assertNull(Ognl.getValue("map[$]", context, root)); assertEquals(root.getArray()[root.getArray().length - 1], Ognl.getValue("map.array[$]", context, root)); assertEquals(root.getMap(), Ognl.getValue("[\"map\"]", context, root)); assertEquals(root.getArray().length, Ognl.getValue("length", context, root.getArray())); assertEquals(root.getList().get(root.getList().size() / 2), Ognl.getValue("getMap().list[|]", context, root)); assertEquals(root.getArray()[2] + root.getMap().size(), Ognl.getValue("map.(array[2] + size())", context, root)); assertEquals(root.getMap(), Ognl.getValue("map.(#this)", context, root)); assertEquals(root.getMap().get(Root.SIZE_STRING), Ognl.getValue("map.(#this != null ? #this['size'] : null)", context, root)); assertEquals(99, Ognl.getValue("map[^].(#this == null ? 'empty' : #this)", context, root)); assertEquals("empty", Ognl.getValue("map[$].(#this == null ? 'empty' : #this)", context, root)); assertEquals(root, Ognl.getValue("map[$].(#root == null ? 'empty' : #root)", context, root)); } @Test void testConditionalExpressions() throws Exception { assertEquals("first", Ognl.getValue("((selected != null) && (currLocale.toString() == selected.toString())) ? 'first' : 'second'", context, root)); assertEquals(Arrays.asList(root.getStringValue(), root.getMap()), Ognl.getValue("{stringValue, getMap()}", context, root)); assertEquals(Arrays.asList("stringValue", root.getMap().get("size")), Ognl.getValue("{'stringValue', map[\"test\"].map[\"size\"]}", context, root)); assertEquals("100(this.checked)", Ognl.getValue("property.bean3.value + '(this.checked)'", context, root)); assertEquals(root.getArray(), Ognl.getValue("getIndexedProperty(property.bean3.map[\"bar\"])", context, root)); assertEquals(((Bean2) root.getProperty()).getBean3(), Ognl.getValue("getProperty().getBean3()", context, root)); } @Test void testIntegerExpressions() throws Exception { assertEquals(0, Ognl.getValue("intValue", context, root)); Ognl.setValue("intValue", context, root, 2); assertEquals(2, Ognl.getValue("intValue", context, root)); } @Test void testBooleanExpressions2() throws Exception { assertEquals(Boolean.TRUE, Ognl.getValue("! booleanValue", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("booleanValue", context, root)); Ognl.setValue("booleanValue", context, root, Boolean.TRUE); assertEquals(Boolean.TRUE, Ognl.getValue("booleanValue", context, root)); } @Test void testMiscExpressions() throws Exception { assertEquals(Boolean.FALSE, Ognl.getValue("! disabled", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("disabled || readonly", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("property.bean3.value != null", context, root)); assertEquals("background-color:blue; width:43px", Ognl.getValue("\"background-color:blue; width:\" + (currentLocaleVerbosity / 2) + \"px\"", context, root)); assertEquals("noborder", Ognl.getValue("renderNavigation ? '' : 'noborder'", context, root)); assertEquals(root.format("key", root.getArray()), Ognl.getValue("format('key', array)", context, root)); assertEquals(root.format("key", 0), Ognl.getValue("format('key', intValue)", context, root)); assertEquals(root.format("key", root.getMap().size()), Ognl.getValue("format('key', map.size)", context, root)); assertEquals("disableButton(this,\"null\");clearElement("testFtpMessage")", Ognl.getValue("'disableButton(this,\"' + map.get('button-testing') + '\");clearElement("testFtpMessage")'", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("!disableWarning", context, root.getMap())); assertEquals(((Bean2) root.getMap().get("value")).getBean3().getValue(), Ognl.getValue("get('value').bean3.value", context, root.getMap())); assertEquals('p', Ognl.getValue("\"Tapestry\".toCharArray()[2]", context, root.getMap())); assertEquals(Boolean.TRUE, Ognl.getValue("nested.deep.last", context, root.getMap())); assertEquals("last foo stop", Ognl.getValue("'last ' + getCurrentClass(@ognl.test.PropertyTest@VALUE)", context, root)); assertEquals(formatValue((int) ((Bean2) root.getProperty()).getMillis(), true, true), Ognl.getValue("@ognl.test.PropertyTest@formatValue(property.millis, true, true)", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("nullObject || !readonly", context, root)); assertEquals(DATETIME_FORMAT.format(root.getTestDate()), Ognl.getValue("testDate == null ? '-' : @ognl.test.PropertyTest@DATETIME_FORMAT.format(testDate)", context, root)); assertEquals("disabled", Ognl.getValue("disabled ? 'disabled' : 'othernot'", context, root)); assertEquals("[ACT]", Ognl.getValue("two.getMessage(active ? 'ACT' : 'INA')", context, bean)); assertEquals(Boolean.TRUE, Ognl.getValue("hasChildren('aaa')", context, bean)); assertEquals(Boolean.FALSE, Ognl.getValue("two.hasChildren('aa')", context, bean)); assertEquals(Boolean.FALSE, Ognl.getValue("two.hasChildren('a')", context, bean)); assertEquals("currentSortAsc", Ognl.getValue("sorted ? (readonly ? 'currentSortDesc' : 'currentSortAsc') : 'currentSortNone'", context, root)); assertEquals("NoIcon", Ognl.getValue("getAsset( (width?'Yes':'No')+'Icon' )", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("flyingMonkey", context, root)); assertEquals("", Ognl.getValue("expiration == null ? '' : @ognl.test.PropertyTest@DATE_FORMAT.format(expiration)", context, root)); assertEquals("javascript:toggle(1);", Ognl.getValue("printDelivery ? 'javascript:toggle(' + bean2.id + ');' : ''", context, root)); assertEquals(Boolean.FALSE, Ognl.getValue("openTransitionWin", context, root)); assertEquals(0, Ognl.getValue("b.methodOfB(a.methodOfA(b)-1)", context, root)); assertEquals(Boolean.TRUE, Ognl.getValue("disabled", context, root)); assertEquals("", Ognl.getValue("value", context, property)); assertEquals("foo", Ognl.getValue("search", context, property)); } public static String formatValue(int millis, boolean b1, boolean b2) { return millis + "-formatted"; } } ================================================ FILE: ognl/src/test/java/ognl/test/ProtectedInnerClassTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ProtectedInnerClassTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root); } @Test void testListSize() throws Exception { Object actual = Ognl.getValue("list.size()", context, root); assertEquals(root.getList().size(), actual); } @Test void testListElement() throws Exception { Object actual = Ognl.getValue("list[0]", context, root); assertEquals(root.getList().get(0), actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/ProtectedMemberTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; class ProtectedMemberTest { protected String _protectedProperty = "protected value"; protected final String _protectedFinalProperty = "protected final value"; protected static String _protectedStaticProperty = "protected static value"; protected static final String _protectedStaticFinalProperty = "protected static final value"; protected OgnlContext context; @BeforeEach void setUp() { // Permit protected access, prevent private and package access context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, true, false)); } protected String getProtectedProperty() { return _protectedProperty; } protected String getProtectedFinalProperty() { return _protectedFinalProperty; } protected static String getProtectedStaticProperty() { return _protectedStaticProperty; } protected static String getProtectedStaticFinalProperty() { return _protectedStaticFinalProperty; } @Test void testProtectedAccessor() throws OgnlException { assertEquals(getProtectedProperty(), Ognl.getValue("protectedProperty", context, this)); } @Test void testProtectedField() throws OgnlException { assertEquals(_protectedProperty, Ognl.getValue("_protectedProperty", context, this)); } @Test void testProtectedFinalAccessor() throws OgnlException { assertEquals(getProtectedFinalProperty(), Ognl.getValue("protectedFinalProperty", context, this)); } @Test void testProtectedFinalField() throws OgnlException { assertEquals(_protectedFinalProperty, Ognl.getValue("_protectedFinalProperty", context, this)); } @Test void testProtectedStaticAccessor() throws OgnlException { assertEquals(getProtectedStaticProperty(), Ognl.getValue("protectedStaticProperty", context, this)); } @Test void testProtectedStaticFieldNormalAccess() { try { assertEquals(_protectedStaticProperty, Ognl.getValue("_protectedStaticProperty", context, this)); fail("Should not be able to access private static _protectedStaticProperty through getValue()"); } catch (OgnlException oex) { assertEquals("_protectedStaticProperty", oex.getMessage()); } } @Test void testProtectedStaticFieldStaticAccess() throws OgnlException { assertEquals(_protectedStaticProperty, OgnlRuntime.getStaticField(context, this.getClass().getName(), "_protectedStaticProperty")); } @Test void testProtectedStaticFinalAccessor() throws OgnlException { assertEquals(getProtectedStaticFinalProperty(), Ognl.getValue("protectedStaticFinalProperty", context, this)); } @Test void testProtectedStaticFinalFieldNormalAccess() { try { assertEquals(_protectedStaticFinalProperty, Ognl.getValue("_protectedStaticFinalProperty", context, this)); fail("Should not be able to access private static _protectedStaticFinalProperty through getValue()"); } catch (OgnlException oex) { assertEquals("_protectedStaticFinalProperty", oex.getMessage()); } } @Test void testProtectedStaticFinalFieldStaticAccess() throws OgnlException { assertEquals(_protectedStaticFinalProperty, OgnlRuntime.getStaticField(context, this.getClass().getName(), "_protectedStaticFinalProperty")); } @Test void testProtectedFieldSet() throws OgnlException { final String originalValue = _protectedProperty; assertEquals(originalValue, Ognl.getValue("_protectedProperty", context, this)); Ognl.setValue("_protectedProperty", context, this, "changevalue"); assertEquals("changevalue", Ognl.getValue("_protectedProperty", context, this)); Ognl.setValue("_protectedProperty", context, this, originalValue); assertEquals(originalValue, Ognl.getValue("_protectedProperty", context, this)); } @Test void testProtectedFinalFieldSet() throws OgnlException { final String originalValue = _protectedFinalProperty; assertEquals(originalValue, Ognl.getValue("_protectedFinalProperty", context, this)); try { Ognl.setValue("_protectedFinalProperty", context, this, "changevalue"); fail("Should not be able to modify final property"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedFinalProperty", oex.getMessage()); } assertEquals(originalValue, Ognl.getValue("_protectedFinalProperty", context, this)); } @Test void testProtectedStaticFieldSet() throws OgnlException { final String originalValue = _protectedStaticProperty; assertEquals(originalValue, OgnlRuntime.getStaticField(context, this.getClass().getName(), "_protectedStaticProperty")); try { Ognl.setValue("_protectedStaticProperty", context, this, "changevalue"); fail("Should not be able to modify static property"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedStaticProperty", oex.getMessage()); } assertEquals(originalValue, OgnlRuntime.getStaticField(context, this.getClass().getName(), "_protectedStaticProperty")); } @Test void testProtectedStaticFinalFieldSet() throws OgnlException { final String originalValue = _protectedStaticFinalProperty; assertEquals(originalValue, OgnlRuntime.getStaticField(context, this.getClass().getName(), "_protectedStaticFinalProperty")); try { Ognl.setValue("_protectedStaticFinalProperty", context, this, "changevalue"); fail("Should not be able to modify static property"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedStaticFinalProperty", oex.getMessage()); } assertEquals(originalValue, OgnlRuntime.getStaticField(context, this.getClass().getName(), "_protectedStaticFinalProperty")); } @Test void testProtectedFieldSetFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { Ognl.setValue("_protectedProperty", context, this, "changevalue"); fail("Should not be able to set protected property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedProperty", oex.getMessage()); } } @Test void testProtectedFinalFieldSetFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { Ognl.setValue("_protectedFinalProperty", context, this, "changevalue"); fail("Should not be able to set protected property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedFinalProperty", oex.getMessage()); } } @Test void testProtectedStaticFieldSetFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { Ognl.setValue("_protectedStaticProperty", context, this, "changevalue"); fail("Should not be able to set protected property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedStaticProperty", oex.getMessage()); } } @Test void testProtectedStaticFinalFieldSetFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { Ognl.setValue("_protectedStaticFinalProperty", context, this, "changevalue"); fail("Should not be able to set protected property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedStaticFinalProperty", oex.getMessage()); } } @Test void testProtectedAccessorFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(getProtectedProperty(), Ognl.getValue("protectedProperty", context, this)); fail("Should not be able to access protected property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest.protectedProperty", oex.getMessage()); } } @Test void testProtectedFieldFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(_protectedProperty, Ognl.getValue("_protectedProperty", context, this)); fail("Should not be able to access protected property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedProperty", oex.getMessage()); } } @Test void testProtectedFinalAccessorFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(getProtectedFinalProperty(), Ognl.getValue("protectedFinalProperty", context, this)); fail("Should not be able to access protected final property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest.protectedFinalProperty", oex.getMessage()); } } @Test void testProtectedFinalFieldFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(_protectedFinalProperty, Ognl.getValue("_protectedFinalProperty", context, this)); fail("Should not be able to access protected final property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedFinalProperty", oex.getMessage()); } } @Test void testProtectedStaticAccessorFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(getProtectedStaticProperty(), Ognl.getValue("protectedStaticProperty", context, this)); fail("Should not be able to access protected static property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest.protectedStaticProperty", oex.getMessage()); } } @Test void testProtectedStaticFieldNormalAccessFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(_protectedStaticProperty, Ognl.getValue("_protectedStaticProperty", context, this)); fail("Should not be able to access protected static property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedStaticProperty", oex.getMessage()); } } @Test void testProtectedStaticFieldStaticAccessFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(_protectedStaticProperty, OgnlRuntime.getStaticField(context, this.getClass().getName(), "_protectedStaticProperty")); fail("Should not be able to access protected static property with protected access turned off"); } catch (OgnlException oex) { assertEquals("Could not get static field _protectedStaticProperty from class ognl.test.ProtectedMemberTest", oex.getMessage()); } } @Test void testProtectedStaticFinalAccessorFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(getProtectedStaticFinalProperty(), Ognl.getValue("protectedStaticFinalProperty", context, this)); fail("Should not be able to access protected static final property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest.protectedStaticFinalProperty", oex.getMessage()); } } @Test void testProtectedStaticFinalFieldNormalAccessFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(_protectedStaticFinalProperty, Ognl.getValue("_protectedStaticFinalProperty", context, this)); fail("Should not be able to access protected static final property with protected access turned off"); } catch (OgnlException oex) { assertEquals("ognl.test.ProtectedMemberTest._protectedStaticFinalProperty", oex.getMessage()); } } @Test void testProtectedStaticFinalFieldStaticAccessFail() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false, false, false)); // Prevent protected access try { assertEquals(_protectedStaticFinalProperty, OgnlRuntime.getStaticField(context, this.getClass().getName(), "_protectedStaticFinalProperty")); fail("Should not be able to access protected static final property with protected access turned off"); } catch (OgnlException oex) { assertEquals("Could not get static field _protectedStaticFinalProperty from class ognl.test.ProtectedMemberTest", oex.getMessage()); } } } ================================================ FILE: ognl/src/test/java/ognl/test/PublicMemberTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; class PublicMemberTest { public String _publicProperty = "public value"; public final String _publicFinalProperty = "public final value"; public static String _publicStaticProperty = "public static value"; public static final String _publicStaticFinalProperty = "public static final value"; protected OgnlContext context; @BeforeEach void setUp() { // Prevent non-public access context = Ognl.createDefaultContext(this, new DefaultMemberAccess(false, false, false)); } public String getPublicProperty() { return _publicProperty; } public String getPublicFinalProperty() { return _publicFinalProperty; } public static String getPublicStaticProperty() { return _publicStaticProperty; } public static String getPublicStaticFinalProperty() { return _publicStaticFinalProperty; } @Test void testPublicAccessor() throws OgnlException { Object actual = Ognl.getValue("publicProperty", context, this); assertEquals(getPublicProperty(), actual); } @Test void testPublicField() throws OgnlException { Object actual = Ognl.getValue("_publicProperty", context, this); assertEquals(_publicProperty, actual); } @Test void testPublicFinalAccessor() throws OgnlException { Object actual = Ognl.getValue("publicFinalProperty", context, this); assertEquals(getPublicFinalProperty(), actual); } @Test void testPublicFinalField() throws OgnlException { Object actual = Ognl.getValue("_publicFinalProperty", context, this); assertEquals(_publicFinalProperty, actual); } @Test void testPublicStaticAccessor() throws OgnlException { Object actual = Ognl.getValue("publicStaticProperty", context, this); assertEquals(getPublicStaticProperty(), actual); } @Test void testPublicStaticFieldNormalAccessFail() { try { Object actual = Ognl.getValue("_publicStaticProperty", context, this); assertEquals(_publicStaticProperty, actual); fail("Should not be able to access public static _publicStaticProperty through getValue()"); } catch (OgnlException oex) { assertEquals("_publicStaticProperty", oex.getMessage()); } } @Test void testPublicStaticFieldStaticAccess() throws OgnlException { Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_publicStaticProperty"); assertEquals(_publicStaticProperty, actual); } @Test void testPublicStaticFinalAccessor() throws OgnlException { Object actual = Ognl.getValue("publicStaticFinalProperty", context, this); assertEquals(getPublicStaticFinalProperty(), actual); } @Test void testPublicStaticFinalFieldNormalAccessFail() { try { Object actual = Ognl.getValue("_publicStaticFinalProperty", context, this); assertEquals(_publicStaticFinalProperty, actual); fail("Should not be able to access public static _publicStaticFinalProperty through getValue()"); } catch (OgnlException oex) { assertEquals("_publicStaticFinalProperty", oex.getMessage()); } } @Test void testPublicStaticFinalFieldStaticAccess() throws OgnlException { Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_publicStaticFinalProperty"); assertEquals(_publicStaticFinalProperty, actual); } @Test void testPublicFieldSet() throws OgnlException { final String originalValue = _publicProperty; Object actual = Ognl.getValue("_publicProperty", context, this); assertEquals(originalValue, actual); Ognl.setValue("_publicProperty", context, this, "changevalue"); actual = Ognl.getValue("_publicProperty", context, this); assertEquals("changevalue", actual); Ognl.setValue("_publicProperty", context, this, originalValue); actual = Ognl.getValue("_publicProperty", context, this); assertEquals(originalValue, actual); } @Test void testPublicFinalFieldSet() throws OgnlException { final String originalValue = _publicFinalProperty; Object actual = Ognl.getValue("_publicFinalProperty", context, this); assertEquals(originalValue, actual); try { Ognl.setValue("_publicFinalProperty", context, this, "changevalue"); fail("Should not be able to modify final property"); } catch (OgnlException oex) { assertEquals("ognl.test.PublicMemberTest._publicFinalProperty", oex.getMessage()); } actual = Ognl.getValue("_publicFinalProperty", context, this); assertEquals(originalValue, actual); } @Test void testPublicStaticFieldSet() throws OgnlException { final String originalValue = _publicStaticProperty; Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_publicStaticProperty"); assertEquals(originalValue, actual); try { Ognl.setValue("_publicStaticProperty", context, this, "changevalue"); fail("Should not be able to modify static property"); } catch (OgnlException oex) { assertEquals("ognl.test.PublicMemberTest._publicStaticProperty", oex.getMessage()); } actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_publicStaticProperty"); assertEquals(originalValue, actual); } @Test void testPublicStaticFinalFieldSet() throws OgnlException { final String originalValue = _publicStaticFinalProperty; Object actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_publicStaticFinalProperty"); assertEquals(originalValue, actual); try { Ognl.setValue("_publicStaticFinalProperty", context, this, "changevalue"); fail("Should not be able to modify static property"); } catch (OgnlException oex) { assertEquals("ognl.test.PublicMemberTest._publicStaticFinalProperty", oex.getMessage()); } actual = OgnlRuntime.getStaticField(context, this.getClass().getName(), "_publicStaticFinalProperty"); assertEquals(originalValue, actual); } } ================================================ FILE: ognl/src/test/java/ognl/test/QuotingTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class QuotingTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } @Test void testCharacterQuoting() throws Exception { assertEquals('c', Ognl.getValue("'c'", context, (Object) null)); assertEquals('s', Ognl.getValue("'s'", context, (Object) null)); } @Test void testStringQuoting() throws Exception { assertEquals("string", Ognl.getValue("'string'", context, (Object) null)); assertEquals("string", Ognl.getValue("\"string\"", context, (Object) null)); assertEquals("bar", Ognl.getValue("'' + 'bar'", context, (Object) null)); assertEquals("yyyy年MM月dd日", Ognl.getValue("'yyyy年MM月dd日'", context, (Object) null)); } } ================================================ FILE: ognl/src/test/java/ognl/test/RaceConditionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.OgnlRuntime; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import java.lang.reflect.Method; import java.util.ArrayList; 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; class RaceConditionTest { @Test void testRaceCondition() throws Exception { runTest(TestAction.class, 1000, 10, Boolean.TRUE); } private static void runTest(Class clazz, int invocationCount, int threadCount, Boolean expected) throws Exception { final ExecutorService executor = Executors.newFixedThreadPool(threadCount); final List> futures = new ArrayList<>(threadCount); for (int i = threadCount; i > 0; i--) { futures.add(executor.submit(new Worker(clazz, invocationCount))); } for (final Future future : futures) { Assertions.assertEquals(expected, future.get()); } executor.shutdown(); executor.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); } private static class TestAction { public String execute() throws Exception { return "success"; } } private static class Worker implements Callable { private final Class clazz; private final int invocationCount; public Worker(final Class clazz, final int invocationCount) { this.clazz = clazz; this.invocationCount = invocationCount; } public Boolean call() throws Exception { for (int i = this.invocationCount; i > 0; i--) { Method method = OgnlRuntime.getMethod(null, clazz, "execute", null, false); if (method == null) { return false; } } return true; } } } ================================================ FILE: ognl/src/test/java/ognl/test/SetterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.InappropriateExpressionException; import ognl.NoSuchPropertyException; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; class SetterTest { private Root root; private OgnlContext context; private Set list; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(null); list = new HashSet<>(); list.add("Test1"); } @Test void testSetNewValue() throws Exception { Ognl.setValue("newValue", context, root.getMap(), 101); assertEquals(101, Ognl.getValue("newValue", context, root.getMap())); Ognl.setValue("newValue", context, root.getMap(), 555); assertEquals(555, Ognl.getValue("newValue", context, root.getMap())); } @Test void testSettableListAbsoluteIndexes() throws Exception { Ognl.setValue("settableList[0]", context, root, "foo"); assertEquals("foo", Ognl.getValue("settableList[0]", context, root)); Ognl.setValue("settableList[0]", context, root, "quux"); assertEquals("quux", Ognl.getValue("settableList[0]", context, root)); } @Test void testSettableListSpecialIndexes() throws Exception { Ognl.setValue("settableList[$]", context, root, "quux"); assertEquals("quux", Ognl.getValue("settableList[$]", context, root)); Ognl.setValue("settableList[$]", context, root, "oompa"); assertEquals("oompa", Ognl.getValue("settableList[$]", context, root)); } @Test void testSetMapValue() throws Exception { Ognl.setValue("map.newValue", context, root, 555); assertEquals(555, Ognl.getValue("map.newValue", context, root)); } @Test void testSetMap() throws Exception { try { Ognl.setValue("map", context, root, new HashMap<>()); fail("Should have thrown NoSuchPropertyException"); } catch (NoSuchPropertyException e) { assertEquals("ognl.test.objects.Root.map", e.getMessage()); } } @Test void testSetSelectedList() { try { Ognl.setValue("selectedList", context, root, list); fail("Should have thrown IllegalArgumentException"); } catch (OgnlException e) { assertEquals(IllegalArgumentException.class, e.getCause().getClass()); assertEquals("Unable to convert type java.util.HashSet of [Test1] to type of java.util.List", e.getCause().getMessage()); assertEquals("selectedList", e.getMessage()); } } @Test void testSetOpenTransitionWin() throws Exception { Ognl.setValue("openTransitionWin", context, root, Boolean.TRUE); assertEquals(Boolean.TRUE, Ognl.getValue("openTransitionWin", context, root)); } @Test void testSetInvalidExpression() throws Exception { try { Ognl.setValue("0", context, null, 0); fail("Should have thrown InappropriateExpressionException"); } catch (InappropriateExpressionException e) { assertEquals("Inappropriate OGNL expression: 0", e.getMessage()); } } } ================================================ FILE: ognl/src/test/java/ognl/test/SetterWithConversionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class SetterWithConversionTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root); } @Test void testIntValueConversion() throws Exception { Ognl.setValue("intValue", context, root, 6.5); assertEquals(6, Ognl.getValue("intValue", context, root)); Ognl.setValue("intValue", context, root, 1025.87645); assertEquals(1025, Ognl.getValue("intValue", context, root)); Ognl.setValue("intValue", context, root, "654"); assertEquals(654, Ognl.getValue("intValue", context, root)); } @Test void testStringValueConversion() throws Exception { Ognl.setValue("stringValue", context, root, 25); assertEquals("25", Ognl.getValue("stringValue", context, root)); Ognl.setValue("stringValue", context, root, 100.25f); assertEquals("100.25", Ognl.getValue("stringValue", context, root)); } @Test void testAnotherStringValueConversion() throws Exception { Ognl.setValue("anotherStringValue", context, root, 0); assertEquals("0", Ognl.getValue("anotherStringValue", context, root)); Ognl.setValue("anotherStringValue", context, root, 0.5); assertEquals("0.5", Ognl.getValue("anotherStringValue", context, root)); } @Test void testAnotherIntValueConversion() throws Exception { Ognl.setValue("anotherIntValue", context, root, "5"); assertEquals(5, Ognl.getValue("anotherIntValue", context, root)); Ognl.setValue("anotherIntValue", context, root, 100.25); assertEquals(100, Ognl.getValue("anotherIntValue", context, root)); } } ================================================ FILE: ognl/src/test/java/ognl/test/ShortCircuitingExpressionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultClassResolver; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.SimpleNode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.fail; class ShortCircuitingExpressionTest> { private C context; @ParameterizedTest @MethodSource("testValues") void shouldShortCircuitExpressionEvaluations(String expression, Object expected) throws OgnlException { assertEquals(expected, Ognl.getValue(expression, null)); } @Test void shouldEvaluateNumber() throws Exception { SimpleNode expression = (SimpleNode) Ognl.compileExpression(context, null, "(#x=99) && #x.doubleValue()"); assertEquals(99.0, Ognl.getValue(expression, context, (Object) null)); } @Test void shouldThrowException() { try { Ognl.getValue("#root ? 99 : someProperty", null); fail(); } catch (Throwable e) { assertInstanceOf(OgnlException.class, e); } } public static class B { private String b; public B(String b) { this.b = b; } public String getB() { return b; } } public static class Params { public B[] params; public Params(B[] params) { this.params = params; } public B[] getParams() { return params; } } @Test void shouldCompare() throws OgnlException { Object root = new Params(new B[]{new B("a")}); C ctx = Ognl.createDefaultContext(null); Object val1 = Ognl.getValue("\"a\".equals(params[0].b)", ctx, root); Object val2 = Ognl.getValue("\"a\".equals(params[0].b)", root); assertEquals(Boolean.TRUE, val1); assertEquals(Boolean.TRUE, val2); } @Test void shouldUseTheSameClassResolver() throws OgnlException { Object root = new Object(); OgnlContext ctx = Ognl.createDefaultContext(root, new MyClassResolver()); Object val1 = Ognl.getValue("@ognl.test.TestClass@getName()", ctx, root); Object val2 = Ognl.getValue("@ognl.test.TestClass@getName()", ctx, (Object) null); assertEquals("name", val1); assertEquals("name", val2); } @Test void shouldThrowExceptionWithWrongClassResolver() throws OgnlException { Object root = new Object(); C oldCtx = Ognl.createDefaultContext(root); C ctx = Ognl.addDefaultContext(root, oldCtx.getMemberAccess(), new MyClassResolver<>(), oldCtx.getTypeConverter(), oldCtx); try { Ognl.getValue("@ognl.test.TestClass@getName()", oldCtx, root); fail(); } catch (OgnlException e) { assertEquals("Method \"getName\" failed for object ognl.test.TestClass", e.getMessage()); } Object val2 = Ognl.getValue("@ognl.test.TestClass@getName()", ctx, root); assertEquals("name", val2); } private static Stream testValues() { return Stream.of( Arguments.of("#root ? someProperty : 99", 99), Arguments.of("(#x=99)? #x.someProperty : #x", null), Arguments.of("#xyzzy.doubleValue()", null), Arguments.of("#xyzzy && #xyzzy.doubleValue()", null), Arguments.of("#xyzzy || 101", 101), Arguments.of("99 || 101", 99) ); } @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } private static class TestClass { public static String getName() { return "name"; } } private static class MyClassResolver> extends DefaultClassResolver { @Override @SuppressWarnings("unchecked") public Class classForName(String className, C context) throws ClassNotFoundException { if (className.equals("ognl.test.TestClass")) { return (Class) TestClass.class; } return super.classForName(className, context); } } } ================================================ FILE: ognl/src/test/java/ognl/test/SimpleNavigationChainTreeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class SimpleNavigationChainTreeTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } @Test void testName() throws Exception { assertTrue(Ognl.isSimpleNavigationChain(Ognl.parseExpression("name"), context)); } @Test void testNameWithIndex() throws Exception { assertFalse(Ognl.isSimpleNavigationChain(Ognl.parseExpression("name[i]"), context)); } @Test void testNameWithAddition() throws Exception { assertFalse(Ognl.isSimpleNavigationChain(Ognl.parseExpression("name + foo"), context)); } @Test void testNameWithProperty() throws Exception { assertTrue(Ognl.isSimpleNavigationChain(Ognl.parseExpression("name.foo"), context)); } } ================================================ FILE: ognl/src/test/java/ognl/test/SimplePropertyTreeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class SimplePropertyTreeTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null); } @Test void testName() throws Exception { assertTrue(Ognl.isSimpleProperty(Ognl.parseExpression("name"), context)); } @Test void testFoo() throws Exception { assertTrue(Ognl.isSimpleProperty(Ognl.parseExpression("foo"), context)); } @Test void testNameWithIndex() throws Exception { assertFalse(Ognl.isSimpleProperty(Ognl.parseExpression("name[i]"), context)); } @Test void testNameWithAddition() throws Exception { assertFalse(Ognl.isSimpleProperty(Ognl.parseExpression("name + foo"), context)); } @Test void testNameWithProperty() throws Exception { assertFalse(Ognl.isSimpleProperty(Ognl.parseExpression("name.foo"), context)); } @Test void testNameWithPropertyChain() throws Exception { assertFalse(Ognl.isSimpleProperty(Ognl.parseExpression("name.foo.bar"), context)); } @Test void testNameWithFilter() throws Exception { assertFalse(Ognl.isSimpleProperty(Ognl.parseExpression("name.{? foo }"), context)); } @Test void testNameWithProjection() throws Exception { assertFalse(Ognl.isSimpleProperty(Ognl.parseExpression("name.( foo )"), context)); } } ================================================ FILE: ognl/src/test/java/ognl/test/StaticsAndConstructorsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test; import ognl.DefaultMemberAccess; import ognl.Node; import ognl.Ognl; import ognl.OgnlContext; import ognl.test.objects.Root; import ognl.test.objects.Simple; import ognl.test.objects.StaticInterface; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class StaticsAndConstructorsTest { private Root root; private OgnlContext context; @BeforeEach void setUp() { root = new Root(); context = Ognl.createDefaultContext(root, new DefaultMemberAccess(true)); } @Test void testClassForName() throws Exception { assertEquals(Object.class, Ognl.getValue("@java.lang.Class@forName(\"java.lang.Object\")", context, root)); } @Test void testIntegerMaxValue() throws Exception { assertEquals(Integer.MAX_VALUE, Ognl.getValue("@java.lang.Integer@MAX_VALUE", context, root)); } @Test void testMaxFunction() throws Exception { assertEquals(4, Ognl.getValue("@@max(3,4)", context, root)); } @Test void testStringBuffer() throws Exception { assertEquals("55", Ognl.getValue("new java.lang.StringBuffer().append(55).toString()", context, root)); } @Test void testClass() throws Exception { assertEquals(root.getClass(), Ognl.getValue("class", context, root)); } @Test void testRootClass() throws Exception { assertEquals(root.getClass(), Ognl.getValue("@ognl.test.objects.Root@class", context, root)); } @Test void testClassName() throws Exception { assertEquals(root.getClass().getName(), Ognl.getValue("class.getName()", context, root)); } @Test void testRootClassName() throws Exception { assertEquals(root.getClass().getName(), Ognl.getValue("@ognl.test.objects.Root@class.getName()", context, root)); } @Test void testRootClassNameProperty() throws Exception { assertEquals(root.getClass().getName(), Ognl.getValue("@ognl.test.objects.Root@class.name", context, root)); } @Test void testClassSuperclass() throws Exception { assertEquals(root.getClass().getSuperclass(), Ognl.getValue("class.getSuperclass()", context, root)); } @Test void testClassSuperclassProperty() throws Exception { assertEquals(root.getClass().getSuperclass(), Ognl.getValue("class.superclass", context, root)); } @Test void testClassNameProperty() throws Exception { assertEquals(root.getClass().getName(), Ognl.getValue("class.name", context, root)); } @Test void testStaticInt() throws Exception { assertEquals(Root.getStaticInt(), Ognl.getValue("getStaticInt()", context, root)); } @Test void testRootStaticInt() throws Exception { assertEquals(Root.getStaticInt(), Ognl.getValue("@ognl.test.objects.Root@getStaticInt()", context, root)); } @Test void testSimpleStringValue() throws Exception { assertEquals(new Simple().getStringValue(), Ognl.getValue("new ognl.test.objects.Simple(property).getStringValue()", context, root)); } @Test void testSimpleStringValueWithMap() throws Exception { assertEquals(new Simple().getStringValue(), Ognl.getValue("new ognl.test.objects.Simple(map['test'].property).getStringValue()", context, root)); } @Test void testMapCurrentClass() throws Exception { Object actual = Ognl.getValue("map.test.getCurrentClass(@ognl.test.StaticsAndConstructorsTest@KEY.toString())", context, root); assertEquals("size stop", actual); } @Test void testIntWrapper() throws Exception { Object actual = Ognl.getValue("new ognl.test.StaticsAndConstructorsTest$IntWrapper(index)", context, root); assertEquals(new IntWrapper(root.getIndex()), actual); } @Test void testIntObjectWrapper() throws Exception { assertEquals(new IntObjectWrapper(root.getIndex()), Ognl.getValue("new ognl.test.StaticsAndConstructorsTest$IntObjectWrapper(index)", context, root)); } @Test void testA() throws Exception { assertEquals(new A(root), Ognl.getValue("new ognl.test.StaticsAndConstructorsTest$A(#root)", context, root)); } @Test void testAnimalsValues() throws Exception { assertTrue((Boolean) Ognl.getValue("@ognl.test.StaticsAndConstructorsTest$Animals@values().length != 2", context, root)); } @Test void testIsOk() throws Exception { assertTrue((Boolean) Ognl.getValue("isOk(@ognl.test.objects.SimpleEnum@ONE, null)", context, root)); } @Test void testStaticMethod() throws Exception { String expressionString = "@ognl.test.objects.StaticInterface@staticMethod()"; Node expression = Ognl.compileExpression(context, root, expressionString); Object actual = Ognl.getValue(expression, context, (Object) null); assertEquals("static", actual); assertEquals(StaticInterface.staticMethod(), actual); } static final String KEY = "size"; public static class IntWrapper { private final int value; public IntWrapper(int value) { this.value = value; } public String toString() { return Integer.toString(value); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; IntWrapper that = (IntWrapper) o; return value == that.value; } } public static class IntObjectWrapper { public IntObjectWrapper(Integer value) { this.value = value; } private final Integer value; public String toString() { return value.toString(); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; IntObjectWrapper that = (IntObjectWrapper) o; return value.equals(that.value); } } public static class A { String key = "A"; public A(Root root) { } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; A a = (A) o; if (key != null ? !key.equals(a.key) : a.key != null) return false; return true; } } public enum Animals { Dog, Cat, Wallabee, Bear } } ================================================ FILE: ognl/src/test/java/ognl/test/VarArgsMethodTest.java ================================================ /* * Copyright 2020 OGNL Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ognl.test; import ognl.Ognl; import ognl.OgnlException; import ognl.test.objects.Simple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; class VarArgsMethodTest { private Simple root; @BeforeEach void setUp() { root = new Simple(); } @Test void testNullVarArgs() throws OgnlException { Object value = Ognl.getValue("isNullVarArgs()", root); assertInstanceOf(String.class, value); assertEquals("null", value); } @Test void testVarArgsWithSingleArg() throws Exception { Object value = Ognl.getValue("isStringVarArgs(new String())", root); assertInstanceOf(String.class, value); assertEquals("args", value); } @Test void testVarArgsWithMultipleArgs() throws Exception { Object value = Ognl.getValue("isStringVarArgs(new String(), new String())", root); assertInstanceOf(String.class, value); assertEquals("args", value); } @Test void testNestedNullVarArgs() throws OgnlException { Object value = Ognl.getValue("get().request()", root); assertInstanceOf(String.class, value); assertEquals("null", value); } @Test void testNestedSingleVarArgs() throws OgnlException { Object value = Ognl.getValue("get().request(new String())", root); assertInstanceOf(String.class, value); assertEquals("args", value); } @Test void testNestedMultipleVarArgs() throws OgnlException { Object value = Ognl.getValue("get().request(new String(), new String())", root); assertInstanceOf(String.class, value); assertEquals("args", value); } } ================================================ FILE: ognl/src/test/java/ognl/test/accessors/ListPropertyAccessorTest.java ================================================ package ognl.test.accessors; import ognl.DefaultMemberAccess; import ognl.ListPropertyAccessor; import ognl.Ognl; import ognl.OgnlContext; import ognl.enhance.ExpressionCompiler; import ognl.test.objects.ListSource; import ognl.test.objects.ListSourceImpl; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; /** * Tests functionality of various built-in object accessors. */ class ListPropertyAccessorTest { private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); } @Test void test_Get_Source_String_Number_Index() { ListPropertyAccessor pa = new ListPropertyAccessor(); Root root = new Root(); context.setRoot(root); context.setCurrentObject(root); context.setCurrentType(Integer.TYPE); assertEquals(".get(0)", pa.getSourceAccessor(context, root.getList(), "0")); assertEquals(List.class, context.getCurrentAccessor()); assertEquals(Object.class, context.getCurrentType()); assertEquals(Integer.TYPE, context.getPreviousType()); assertNull(context.getPreviousAccessor()); } @Test void test_Get_Source_Object_Number_Index() { ListPropertyAccessor pa = new ListPropertyAccessor(); Root root = new Root(); context.setRoot(root); context.setCurrentObject(root); context.setCurrentType(Integer.class); assertEquals(".get(indexValue.intValue())", pa.getSourceAccessor(context, root.getList(), "indexValue")); assertEquals(List.class, context.getCurrentAccessor()); assertEquals(Object.class, context.getCurrentType()); assertEquals(Integer.class, context.getPreviousType()); assertNull(context.getPreviousAccessor()); } @Test void test_List_To_Object_Property_Accessor_Read() { ListPropertyAccessor pa = new ListPropertyAccessor(); ListSource list = new ListSourceImpl(); context.setRoot(list); context.setCurrentObject(list); assertEquals(".getTotal()", pa.getSourceAccessor(context, list, "total")); assertNull(context.get(ExpressionCompiler.PRE_CAST)); assertEquals(int.class, context.getCurrentType()); assertEquals(ListSource.class, context.getCurrentAccessor()); } } ================================================ FILE: ognl/src/test/java/ognl/test/accessors/PropertyAccessTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.accessors; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import ognl.test.objects.BeanProvider; import ognl.test.objects.BeanProviderAccessor; import ognl.test.objects.EvenOdd; import ognl.test.objects.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PropertyAccessTest { @BeforeEach void setUp() { OgnlRuntime.setPropertyAccessor(BeanProvider.class, new BeanProviderAccessor()); } @Test void testPropertyAccess() throws OgnlException { // given OgnlContext context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); Root root = new Root(); root.getBeans().setBean("evenOdd", new EvenOdd()); // when Object result = Ognl.getValue("beans.evenOdd.next", context, root); // then assertEquals("even", result); } } ================================================ FILE: ognl/src/test/java/ognl/test/enhance/ExpressionCompilerTest.java ================================================ /** * */ package ognl.test.enhance; import ognl.DefaultMemberAccess; import ognl.ExpressionSyntaxException; import ognl.Node; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import ognl.enhance.ExpressionCompiler; import ognl.enhance.OgnlExpressionCompiler; import ognl.test.objects.Bean1; import ognl.test.objects.GenericRoot; import ognl.test.objects.IndexedMapObject; import ognl.test.objects.Inherited; import ognl.test.objects.Root; import ognl.test.objects.TestInherited1; import ognl.test.objects.TestInherited2; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collection; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** * Tests functionality of {@link ExpressionCompiler}. */ public class ExpressionCompilerTest { private OgnlExpressionCompiler compiler; private OgnlContext context; @BeforeEach void setUp() { context = Ognl.createDefaultContext(null, new DefaultMemberAccess(false)); compiler = new ExpressionCompiler(); } @Test void test_Get_Property_Access() throws Throwable { Node expr = (Node) Ognl.parseExpression("bean2"); Bean1 root = new Bean1(); compiler.compileExpression(context, expr, root); assertNotNull(expr.getAccessor().get(context, root)); } @Test void test_Get_Indexed_Property() throws Throwable { Node expr = (Node) Ognl.parseExpression("bean2.bean3.indexedValue[25]"); Bean1 root = new Bean1(); assertNull(Ognl.getValue(expr, context, root)); compiler.compileExpression(context, expr, root); assertNull(expr.getAccessor().get(context, root)); } @Test void test_Set_Indexed_Property() throws Throwable { Node expr = (Node) Ognl.parseExpression("bean2.bean3.indexedValue[25]"); Bean1 root = new Bean1(); assertNull(Ognl.getValue(expr, context, root)); compiler.compileExpression(context, expr, root); expr.getAccessor().set(context, root, "test string"); assertEquals("test string", expr.getAccessor().get(context, root)); } @Test void test_Expression() throws Throwable { Node expr = (Node) Ognl.parseExpression("bean2.bean3.value <= 24"); Bean1 root = new Bean1(); assertEquals(Boolean.FALSE, Ognl.getValue(expr, context, root)); compiler.compileExpression(context, expr, root); assertEquals(Boolean.FALSE, expr.getAccessor().get(context, root)); } @Test void test_Get_Context_Property() throws Throwable { context.put("key", "foo"); Node expr = (Node) Ognl.parseExpression("bean2.bean3.map[#key]"); Bean1 root = new Bean1(); assertEquals("bar", Ognl.getValue(expr, context, root)); compiler.compileExpression(context, expr, root); assertEquals("bar", expr.getAccessor().get(context, root)); context.put("key", "bar"); assertEquals("baz", Ognl.getValue(expr, context, root)); assertEquals("baz", expr.getAccessor().get(context, root)); } @Test void test_Set_Context_Property() throws Throwable { context.put("key", "foo"); Node expr = (Node) Ognl.parseExpression("bean2.bean3.map[#key]"); Bean1 root = new Bean1(); compiler.compileExpression(context, expr, root); assertEquals("bar", expr.getAccessor().get(context, root)); context.put("key", "bar"); assertEquals("baz", expr.getAccessor().get(context, root)); expr.getAccessor().set(context, root, "bam"); assertEquals("bam", expr.getAccessor().get(context, root)); } @Test void test_Property_Index() throws Throwable { Root root = new Root(); Node expr = Ognl.compileExpression(context, root, "{index + 1}"); Object ret = expr.getAccessor().get(context, root); assertInstanceOf(Collection.class, ret); } @Test void test_Root_Expression_Inheritance() throws Throwable { Inherited obj1 = new TestInherited1(); Inherited obj2 = new TestInherited2(); Node expr = Ognl.compileExpression(context, obj1, "myString"); assertEquals("inherited1", expr.getAccessor().get(context, obj1)); assertEquals("inherited2", expr.getAccessor().get(context, obj2)); } @Test void test_Create_Empty_Collection() throws Throwable { Node expr = Ognl.compileExpression(context, null, "{}"); Object ret = expr.getAccessor().get(context, null); assertNotNull(ret); assertTrue(Collection.class.isAssignableFrom(ret.getClass())); } public String getKey() { return "key"; } @Test void test_Indexed_Property() throws Throwable { Node expression = Ognl.compileExpression(context, this, "key"); assertEquals("key", expression.getAccessor().get(context, this)); } IndexedMapObject mapObject = new IndexedMapObject("propertyValue"); public IndexedMapObject getObject() { return mapObject; } @SuppressWarnings("unused") public String getPropertyKey() { return "property"; } @Test void test_Indexed_Map_Property() throws Throwable { assertEquals("propertyValue", Ognl.getValue("object[propertyKey]", this)); context.clear(); Node expression = Ognl.compileExpression(context, this, "object[#this.propertyKey]"); assertEquals("propertyValue", expression.getAccessor().get(context, this)); context.clear(); expression = Ognl.compileExpression(context, this, "object[propertyKey]"); assertEquals("propertyValue", expression.getAccessor().get(context, this)); } @Test void test_Set_Generic_Property() throws Exception { GenericRoot root = new GenericRoot(); Node node = Ognl.compileExpression(context, root, "cracker.param"); assertNull(node.getAccessor().get(context, root)); node.getAccessor().set(context, root, 0); assertEquals(0, node.getAccessor().get(context, root)); node.getAccessor().set(context, root, 12); assertEquals(12, node.getAccessor().get(context, root)); } /** * Test ApplyExpressionMaxLength() mechanism for OGNL expression parsing. */ @Test void test_ApplyExpressionMaxLength() { final String shortFakeExpression = new String(new char[10]).replace('\0', 'S'); final String mediumFakeExpression = new String(new char[100]).replace('\0', 'S'); final String longFakeExpression = new String(new char[1000]).replace('\0', 'S'); final String veryLongFakeExpression = new String(new char[10000]).replace('\0', 'S'); try { // --------------------------------------------------------------------- // Test initial default state. Any length expression should work try { Ognl.parseExpression(shortFakeExpression); } catch (Exception ex) { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(mediumFakeExpression); } catch (Exception ex) { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(longFakeExpression); } catch (Exception ex) { fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(veryLongFakeExpression); } catch (Exception ex) { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Confirm illegal length values are rejected try { Ognl.applyExpressionMaxLength(Integer.MIN_VALUE); fail("applyExpressionMaxLength illegal value " + Integer.MIN_VALUE + " was permitted ?"); } catch (IllegalArgumentException iaex) { // Expected result } catch (Exception ex) { fail("applyExpressionMaxLength illegal value " + Integer.MIN_VALUE + " failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Confirm maximum length value are accepted try { Ognl.applyExpressionMaxLength(Integer.MAX_VALUE); } catch (Exception ex) { fail("applyExpressionMaxLength value " + Integer.MAX_VALUE + " failed unexpectedly - Error: " + ex); } // Test state with maximum length limit. Any length expression should work try { Ognl.parseExpression(shortFakeExpression); } catch (Exception ex) { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(mediumFakeExpression); } catch (Exception ex) { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(longFakeExpression); } catch (Exception ex) { fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(veryLongFakeExpression); } catch (Exception ex) { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Confirm all lengths up to and equal to the largest testing string are accepted try { Ognl.applyExpressionMaxLength(veryLongFakeExpression.length()); } catch (Exception ex) { fail("applyExpressionMaxLength value " + veryLongFakeExpression.length() + " failed unexpectedly - Error: " + ex); } // Test state with veryLongFakeExpression.length() limit. All tested length expressions should work try { Ognl.parseExpression(shortFakeExpression); } catch (Exception ex) { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(mediumFakeExpression); } catch (Exception ex) { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(longFakeExpression); } catch (Exception ex) { fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(veryLongFakeExpression); } catch (Exception ex) { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Confirm all lengths less than the largest testing string length are accepted try { Ognl.applyExpressionMaxLength(veryLongFakeExpression.length() - 1); } catch (Exception ex) { fail("applyExpressionMaxLength value " + (veryLongFakeExpression.length() - 1) + " failed unexpectedly - Error: " + ex); } // Test state with veryLongFakeExpression.length() -1 limit. Only veryLongFakeExpression should fail try { Ognl.parseExpression(shortFakeExpression); } catch (Exception ex) { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(mediumFakeExpression); } catch (Exception ex) { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(longFakeExpression); } catch (Exception ex) { fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(veryLongFakeExpression); fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") succeeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Confirm all lengths greater than the shortest testing string length are rejected try { Ognl.applyExpressionMaxLength(shortFakeExpression.length()); } catch (Exception ex) { fail("applyExpressionMaxLength value " + shortFakeExpression.length() + " failed unexpectedly - Error: " + ex); } // Test state with shortFakeExpression.length() limit. Only shortFakeExpression should succeed try { Ognl.parseExpression(shortFakeExpression); } catch (Exception ex) { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(mediumFakeExpression); fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(longFakeExpression); fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of longFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(veryLongFakeExpression); fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Confirm even the shortest testing string length is rejected try { Ognl.applyExpressionMaxLength(shortFakeExpression.length() - 1); } catch (Exception ex) { fail("applyExpressionMaxLength value " + (shortFakeExpression.length() - 1) + " failed unexpectedly - Error: " + ex); } // Test state with shortFakeExpression.length() limit. Only shortFakeExpression should succeed try { Ognl.parseExpression(shortFakeExpression); fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(mediumFakeExpression); fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(longFakeExpression); fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of longFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(veryLongFakeExpression); fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Confirm only the empty string is not rejected try { Ognl.applyExpressionMaxLength(0); } catch (Exception ex) { fail("applyExpressionMaxLength value 0 failed unexpectedly - Error: " + ex); } // Test state with 0 length limit. Only the empty string should succeed try { Ognl.parseExpression(""); } catch (ExpressionSyntaxException esx) { // Expected for an empty expression (acceptable state). } catch (Exception ex) { fail("Parse of empty string failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(shortFakeExpression); fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of shortFakeExpression (" + shortFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(mediumFakeExpression); fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of mediumFakeExpression (" + mediumFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(longFakeExpression); fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of longFakeExpression (" + longFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of longFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } try { Ognl.parseExpression(veryLongFakeExpression); fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") succeded unexpectedly after limit set below its length ?"); } catch (OgnlException oex) { if (oex.getCause() instanceof SecurityException) { assertInstanceOf(SecurityException.class, oex.getCause()); } else { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly after limit set below its length - Error: " + oex); } } catch (Exception ex) { fail("Parse of veryLongFakeExpression (" + veryLongFakeExpression.length() + ") failed unexpectedly - Error: " + ex); } } finally { try { Ognl.applyExpressionMaxLength(null); // Reset to default state before leaving test. } catch (Exception ex) { // ignore, do not care for cleanup } } } /** * Test freezing and thawing of maximum expression length mechanism for OGNL expression parsing. */ @Test void test_FreezeThawExpressionMaxLength() { try { // --------------------------------------------------------------------- // Test initial default state. Can change maximum length to valid values without any issues try { Ognl.applyExpressionMaxLength(0); Ognl.applyExpressionMaxLength(Integer.MAX_VALUE); Ognl.applyExpressionMaxLength(0); Ognl.applyExpressionMaxLength(10000); Ognl.applyExpressionMaxLength(1000); Ognl.applyExpressionMaxLength(100); Ognl.applyExpressionMaxLength(10); } catch (Exception ex) { fail("applyExpressionMaxLength in default (initial) state with legal values failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Test thawing permitted even if never frozen, does not prevent setting afterward try { Ognl.thawExpressionMaxLength(); Ognl.applyExpressionMaxLength(Integer.MAX_VALUE); Ognl.thawExpressionMaxLength(); } catch (IllegalStateException ise) { fail("applyExpressionMaxLength was blocked when thawed ?"); // Expected result } catch (Exception ex) { fail("applyExpressionMaxLength (thaw attempt) failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Test freezing maximum length try { Ognl.freezeExpressionMaxLength(); Ognl.applyExpressionMaxLength(Integer.MAX_VALUE); fail("applyExpressionMaxLength was not blocked when frozen ?"); } catch (IllegalStateException ise) { // Expected result } catch (Exception ex) { fail("applyExpressionMaxLength (freeze attempt) failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Test repetative freezing try { Ognl.freezeExpressionMaxLength(); Ognl.freezeExpressionMaxLength(); } catch (Exception ex) { fail("freezeExpressionMaxLength failed during repetative freeze operations - Error: " + ex); } // --------------------------------------------------------------------- // Confirm still frozen, then thaw and demonstrate set permitted try { Ognl.applyExpressionMaxLength(Integer.MAX_VALUE); fail("applyExpressionMaxLength was not blocked when frozen ?"); } catch (IllegalStateException ise) { // Expected result } catch (Exception ex) { fail("applyExpressionMaxLength (when frozen) failed unexpectedly - Error: " + ex); } try { Ognl.thawExpressionMaxLength(); Ognl.applyExpressionMaxLength(Integer.MAX_VALUE); } catch (IllegalStateException ise) { fail("applyExpressionMaxLength was blocked when thawed ?"); // Expected result } catch (Exception ex) { fail("applyExpressionMaxLength (thaw attempt) failed unexpectedly - Error: " + ex); } // --------------------------------------------------------------------- // Test repetitive thawing try { Ognl.thawExpressionMaxLength(); Ognl.thawExpressionMaxLength(); } catch (Exception ex) { fail("thawExpressionMaxLength failed during repetative thaw operations - Error: " + ex); } } finally { try { Ognl.thawExpressionMaxLength(); // Reset to default state before leaving test. } catch (Exception ex) { // ignore, do not care for cleanup } try { Ognl.applyExpressionMaxLength(null); // Reset to default state before leaving test. } catch (Exception ex) { // ignore, do not care for cleanup } } } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/BaseBean.java ================================================ /** * */ package ognl.test.objects; /** * Base class used to test inheritance class casting. */ public abstract class BaseBean { public abstract String getName(); public boolean getActive() { return true; } public boolean isActive2() { return true; } public Two getTwo() { return new Two(); } public String getMessage(String mes) { return "[" + mes + "]"; } public boolean hasChildren(String name) { return name.length() > 2; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/BaseGeneric.java ================================================ package ognl.test.objects; import java.io.Serializable; /** * Used to test ognl handling of java generics. */ public class BaseGeneric { E _value; GenericService _service; protected I[] ids; public BaseGeneric() { _service = new GenericServiceImpl(); } public void setIds(I[] ids) { this.ids = ids; } public I[] getIds() { return this.ids; } public String getMessage() { return "Message"; } public E getValue() { return _value; } public GenericService getService() { return _service; } public String format(Object value) { return value.toString(); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/BaseIndexed.java ================================================ package ognl.test.objects; /** * Class used to test inheritance. */ public class BaseIndexed { public Object getLine(int index) { return "line:" + index; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/BaseObjectIndexed.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; import java.util.HashMap; import java.util.Map; public class BaseObjectIndexed extends Object { private Map attributes = new HashMap(); public BaseObjectIndexed() { super(); } public Map getAttributes() { return attributes; } public Object getAttribute(String name) { return attributes.get(name); } public void setAttribute(String name, Object value) { attributes.put(name, value); } /* allow testing property name where types do not match */ public Object getOtherAttribute(String name) { return null; } public void setOtherAttribute(Object someObject, Object foo) { /* do nothing */ } /* test whether get only is found */ public Object getSecondaryAttribute(Object name) { return attributes.get(name); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/BaseSyntheticObject.java ================================================ package ognl.test.objects; import java.util.ArrayList; import java.util.List; /** * Used to test OGNL-136 use of synthetic methods. */ public abstract class BaseSyntheticObject { protected List getList() { return new ArrayList(); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Bean1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; public class Bean1 extends Object { private Bean2 bean2 = new Bean2(); public Bean2 getBean2() { return bean2; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Bean2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; public class Bean2 extends Object { private Bean3 bean3 = new Bean3(); private boolean _pageBreakAfter = false; public String code = "code"; public Long getId() { return 1l; } public Bean3 getBean3() { return bean3; } public long getMillis() { return 1000 * 60 * 2; } public boolean isCarrier() { return false; } public boolean isPageBreakAfter() { return _pageBreakAfter; } public void setPageBreakAfter(boolean value) { _pageBreakAfter = value; } public void togglePageBreakAfter() { _pageBreakAfter ^= true; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Bean2 bean2 = (Bean2) o; if (_pageBreakAfter != bean2._pageBreakAfter) return false; return true; } public int hashCode() { return (_pageBreakAfter ? 1 : 0); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Bean3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; import java.util.HashMap; import java.util.Map; public class Bean3 extends Object { private int value = 100; private Map map; { map = new HashMap(); map.put("foo", "bar"); map.put("bar", "baz"); } private String _nullValue; private Object _indexValue; public int getValue() { return value; } public void setValue(int value) { this.value = value; } public Object getIndexedValue(int index) { return _indexValue; } public void setIndexedValue(int index, Object value) { _indexValue = value; } public Map getMap() { return map; } public void setNullValue(String value) { _nullValue = value; } public String getNullValue() { return _nullValue; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((_indexValue == null) ? 0 : _indexValue.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final Bean3 other = (Bean3) obj; if (_indexValue == null) { if (other._indexValue != null) return false; } else if (!_indexValue.equals(other._indexValue)) return false; return true; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/BeanProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; /** * Test interface to be used with a custom property accessor. */ public interface BeanProvider { /** * Gets a bean by name. */ Object getBean(String name); /** * Sets a new bean mapping. */ void setBean(String name, Object bean); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/BeanProviderAccessor.java ================================================ /** * */ package ognl.test.objects; import ognl.ObjectPropertyAccessor; import ognl.OgnlContext; import ognl.OgnlException; import ognl.OgnlRuntime; import ognl.PropertyAccessor; import ognl.enhance.ExpressionCompiler; import ognl.enhance.UnsupportedCompilationException; /** * Implementation of provider that works with {@link BeanProvider} instances. */ public class BeanProviderAccessor> extends ObjectPropertyAccessor implements PropertyAccessor { public Object getProperty(C context, Object target, Object name) throws OgnlException { BeanProvider provider = (BeanProvider) target; String beanName = (String) name; return provider.getBean(beanName); } /** * Returns true if the name matches a bean provided by the provider. * Otherwise invokes the super implementation. **/ public boolean hasGetProperty(C context, Object target, Object oname) throws OgnlException { BeanProvider provider = (BeanProvider) target; String beanName = ((String) oname).replaceAll("\"", ""); return provider.getBean(beanName) != null; } public String getSourceAccessor(C context, Object target, Object name) { BeanProvider provider = (BeanProvider) target; String beanName = ((String) name).replaceAll("\"", ""); if (provider.getBean(beanName) != null) { context.setCurrentAccessor(BeanProvider.class); context.setCurrentType(provider.getBean(beanName).getClass()); ExpressionCompiler.addCastString(context, "((" + OgnlRuntime.getCompiler().getInterfaceClass(provider.getBean(beanName).getClass()).getName() + ")"); return ".getBean(\"" + beanName + "\"))"; } return super.getSourceAccessor(context, target, name); } public String getSourceSetter(C context, Object target, Object name) { throw new UnsupportedCompilationException("Can't set beans on BeanProvider."); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/BeanProviderImpl.java ================================================ /** * */ package ognl.test.objects; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Implementation of {@link BeanProvider}. */ public class BeanProviderImpl implements Serializable, BeanProvider { private Map _map = new HashMap(); public BeanProviderImpl() { } public Object getBean(String name) { return _map.get(name); } public void setBean(String name, Object bean) { _map.put(name, bean); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Component.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; public class Component extends Object { private URLStorage toDisplay = new URLStorage(); private Page page = new Page(); public static class URLStorage extends Object { private String pictureUrl = "http://www.picturespace.com/pictures/100"; public String getPictureUrl() { return pictureUrl; } public void setPictureUrl(String value) { pictureUrl = value; } } public static class Page extends Object { public Object createRelativeAsset(String value) { return "/toplevel/" + value; } } public Component() { super(); } public Page getPage() { return page; } public void setPage(Page value) { page = value; } public URLStorage getToDisplay() { return toDisplay; } public void setToDisplay(URLStorage value) { toDisplay = value; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/ComponentImpl.java ================================================ package ognl.test.objects; /** * */ public class ComponentImpl implements IComponent { String _clientId; int _count = 0; public String getClientId() { return _clientId; } public void setClientId(String id) { _clientId = id; } public int getCount(String index) { return _count; } public void setCount(String index, int count) { _count = count; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/ComponentSubclass.java ================================================ package ognl.test.objects; /** * */ public class ComponentSubclass extends ComponentImpl { int _count = 0; public int getCount() { return _count; } public void setCount(int count) { _count = count; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Copy.java ================================================ package ognl.test.objects; /** * */ public class Copy { public int size() { return 1; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/CorrectedObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; public class CorrectedObject { public CorrectedObject() { } public void setStringValue(String value) { } public String getStringValue() { return null; } public String getIndexedStringValue(String key) { return null; } public void setIndexedStringValue(String key, String value) { } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Cracker.java ================================================ package ognl.test.objects; import java.io.Serializable; /** * Generic test object. */ public interface Cracker { T getParam(); void setParam(T param); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Entry.java ================================================ package ognl.test.objects; /** * */ public class Entry { private int _size = 1; public int size() { return _size; } public Copy getCopy() { return new Copy(); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Entry entry = (Entry) o; return _size == entry._size; } public int hashCode() { return _size; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/EvenOdd.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; public class EvenOdd { private boolean even = true; /** * Returns "even" or "odd". Whatever it returns on one invocation, it will * return the opposite on the next. By default, the first value returned is * "even". */ public String getNext() { String result = even ? "even" : "odd"; even = !even; return result; } public boolean isEven() { return even; } /** * Overrides the even flag. */ public void setEven(boolean value) { even = value; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/FirstBean.java ================================================ /** * */ package ognl.test.objects; /** * */ public class FirstBean extends BaseBean { public String getName() { return "FirstBean"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/FormComponentImpl.java ================================================ package ognl.test.objects; /** * */ public class FormComponentImpl extends ComponentImpl implements IFormComponent { IForm _form; public IForm getForm() { return _form; } public void setForm(IForm form) { _form = form; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/FormImpl.java ================================================ package ognl.test.objects; /** * */ public class FormImpl extends ComponentImpl implements IForm { } ================================================ FILE: ognl/src/test/java/ognl/test/objects/GameGeneric.java ================================================ package ognl.test.objects; /** * */ public class GameGeneric extends BaseGeneric { public GameGeneric() { _value = new GameGenericObject(); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/GameGenericObject.java ================================================ package ognl.test.objects; /** * */ public class GameGenericObject implements GenericObject { public GameGenericObject() { super(); } public int getId() { return 20; } public String getDisplayName() { return "Halo 3"; } public String getHappy() { return "happy"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/GenericCracker.java ================================================ package ognl.test.objects; /** * */ public class GenericCracker implements Cracker { Integer _param; public Integer getParam() { return _param; } public void setParam(Integer param) { _param = param; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/GenericObject.java ================================================ package ognl.test.objects; /** * Used by {@link BaseGeneric} to reference a class type. */ public interface GenericObject { int getId(); String getDisplayName(); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/GenericRoot.java ================================================ package ognl.test.objects; /** * */ public class GenericRoot { Root _root = new Root(); GenericCracker _cracker = new GenericCracker(); public Root getRoot() { return _root; } public void setRoot(Root root) { _root = root; } public GenericCracker getCracker() { return _cracker; } public void setCracker(GenericCracker cracker) { _cracker = cracker; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/GenericService.java ================================================ package ognl.test.objects; public interface GenericService { String getFullMessageFor(PersonGenericObject person, Object... arguments); String getFullMessageFor(GameGenericObject game, Object... arguments); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/GenericServiceImpl.java ================================================ package ognl.test.objects; public class GenericServiceImpl implements GenericService { public String getFullMessageFor(GameGenericObject game, Object... arguments) { game.getHappy(); return game.getDisplayName(); } public String getFullMessageFor(PersonGenericObject person, Object... arguments) { return person.getDisplayName(); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/GetterMethods.java ================================================ package ognl.test.objects; /** * */ public class GetterMethods { private int theInt = 1; public boolean isAllowDisplay(Object something) { return true; } public int getAllowDisplay() { return theInt; } public void setAllowDisplay(int val) { theInt = val; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/IComponent.java ================================================ package ognl.test.objects; /** * */ public interface IComponent { String getClientId(); void setClientId(String id); int getCount(String index); void setCount(String index, int count); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/IContentProvider.java ================================================ package ognl.test.objects; import java.util.List; /** * */ public interface IContentProvider { public List getElements(); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/IForm.java ================================================ package ognl.test.objects; /** * */ public interface IForm extends IComponent { } ================================================ FILE: ognl/src/test/java/ognl/test/objects/IFormComponent.java ================================================ package ognl.test.objects; /** * */ public interface IFormComponent extends IComponent { String getClientId(); IForm getForm(); void setForm(IForm form); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/ITreeContentProvider.java ================================================ package ognl.test.objects; import java.util.Collection; /** * */ public interface ITreeContentProvider extends IContentProvider { public Collection getChildren(Object parentElement); public boolean hasChildren(Object parentElement); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Indexed.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class Indexed extends BaseIndexed { private String[] _values = new String[]{"foo", "bar", "baz"}; private final List _list = new ArrayList<>(); private final ListSource _source = new ListSourceImpl(); private Map _props = new HashMap(); public Indexed() { _list.add(1); _list.add(2); _list.add(3); _source.addValue(new Bean2()); } public Indexed(String[] values) { _values = values; } /* Indexed property "_values" */ public String[] getValues() { return _values; } public void setValues(String[] value) { _values = value; } /** * This method returns the string from the array and appends "xxx" to * distinguish the "get" method from the direct array access. */ public String getValues(int index) { return _values[index] + "xxx"; } public void setValues(int index, String value) { if (value.endsWith("xxx")) { _values[index] = value.substring(0, value.length() - 3); } else { _values[index] = value; } } public Collection getList() { return _list; } public String getTitle(int count) { return "Title count " + count; } public ListSource getSource() { return _source; } public void setProperty(String property, Object value) { _props.put(property, value); } public Object getProperty(String property) { return _props.get(property); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/IndexedMapObject.java ================================================ package ognl.test.objects; /** * Simple object used to test indexed map references using "#this" references. */ public class IndexedMapObject { String property; public IndexedMapObject(String property) { this.property = property; } public String getProperty() { return property; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/IndexedSetObject.java ================================================ package ognl.test.objects; import java.util.HashMap; /** * Test for OGNL-119. */ public class IndexedSetObject { private final HashMap things = new HashMap(); public IndexedSetObject() { things.put("x", new Container(1)); } public Object getThing(String index) { return things.get(index); } public void setThing(String index, Object value) { things.put(index, value); } public static class Container { private int val; public Container(int val) { this.val = val; } public int getVal() { return val; } public void setVal(int val) { this.val = val; } } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Inherited.java ================================================ package ognl.test.objects; /** * */ public interface Inherited { String getMyString(); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/ListSource.java ================================================ package ognl.test.objects; public interface ListSource { int getTotal(); Object addValue(Object value); Object getName(); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/ListSourceImpl.java ================================================ package ognl.test.objects; import java.io.Serial; import java.util.ArrayList; public class ListSourceImpl extends ArrayList implements ListSource { @Serial private static final long serialVersionUID = 6144140702137776331L; public ListSourceImpl() { } public int getTotal() { return super.size(); } public Object addValue(Object value) { return super.add(value); } public Object getName() { return null; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/MenuItem.java ================================================ package ognl.test.objects; import java.util.ArrayList; import java.util.List; /** * */ public class MenuItem { private String page; private String label; private List children = new ArrayList(); public MenuItem(String page, String label) { this(page, label, new ArrayList()); } public MenuItem(String page, String label, List children) { this.page = page; this.label = label; this.children = children; } public List getChildren() { return children; } public String getLabel() { return label; } public String getPage() { return page; } public String toString() { StringBuffer sb = new StringBuffer("MenuItem["); sb.append("page=" + getPage()); sb.append(",label=" + getLabel()); sb.append(",children=" + getChildren().size()); sb.append("]"); return sb.toString(); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Messages.java ================================================ package ognl.test.objects; import java.util.Map; /** * */ public class Messages { Map _source; public Messages(Map source) { _source = source; } public String getMessage(String key) { return (String) _source.get(key); } public String format(String key, Object[] parms) { return "foo"; } public String format(String key, Object param1, Object param2, Object param3) { return "blah"; } public String format(String key, Object param1) { return "first"; } public String format(String key, Object param1, Object param2) { return "haha"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/MethodTestMethods.java ================================================ package ognl.test.objects; import java.util.Arrays; import java.util.List; public class MethodTestMethods { //--------------------------------------------------------------------- // TestCase for https://github.com/jkuhnert/ognl/issues/17 - ArrayIndexOutOfBoundsException when trying to access BeanFactory // Implementation of BeanFactory interface //--------------------------------------------------------------------- public Object getBean(String name) { return "NamedBean: " + name; } public T getBean(String name, Class requiredType) { return (T) ("NamedTypedBean: " + name + " " + requiredType.getSimpleName()); } public T getBean(Class requiredType) { return (T) ("TypedBean: " + requiredType.getSimpleName()); } public Object getBean(String name, Object... args) { return "NamedBeanWithArgs: " + name + " " + Arrays.toString(args); } //--------------------------------------------------------------------- // https://issues.apache.org/jira/browse/OGNL-250 - OnglRuntime getMethodValue fails to find method matching propertyName //--------------------------------------------------------------------- private String testProperty = "Hello World!"; public String testProperty() { return testProperty; } //--------------------------------------------------------------------- // Tests related to https://github.com/jkuhnert/ognl/issues/16 // Argument matching tests //--------------------------------------------------------------------- public String argsTest1(Object[] data) { return "Array: " + Arrays.toString(data); } public String argsTest2(List data) { return "List: " + data; } public String argsTest3(Object[] data) { return "Array: " + Arrays.toString(data); } public String argsTest3(List data) { return "List: " + data; } //--------------------------------------------------------------------- // https://github.com/jkuhnert/ognl/issues/23 // 'avg' tests //--------------------------------------------------------------------- public double avg(final Iterable target) { double total = 0; int size = 0; for (final Number element : target) { total += element.doubleValue(); size++; } return total / size; } public double avg(final Number[] target) { double total = 0; for (final Number element : target) { total += element.doubleValue(); } return total / target.length; } public double avg(final byte[] target) { double total = 0; for (final Number element : target) { total += element.doubleValue(); } return total / target.length; } public double avg(final short[] target) { double total = 0; for (final Number element : target) { total += element.doubleValue(); } return total / target.length; } public double avg(final int[] target) { double total = 0; for (final Number element : target) { total += element.doubleValue(); } return total / target.length; } public double avg(final long[] target) { double total = 0; for (final Number element : target) { total += element.doubleValue(); } return total / target.length; } public double avg(final float[] target) { double total = 0; for (final Number element : target) { total += element.doubleValue(); } return total / target.length; } public double avg(final double[] target) { double total = 0; for (final Number element : target) { total += element.doubleValue(); } return total / target.length; } public String[] getStringArray() { return new String[]{"Hello", "World"}; } public List getStringList() { return Arrays.asList("Hello", "World"); } public List getObjectList() { return Arrays.asList((Object) "Object"); } public String showList(String[] args) { return "Strings: " + Arrays.toString(args); } public String showList(Object[] args) { return "Objects: " + Arrays.toString(args); } public String showStringList(String[] args) { return "Strings: " + Arrays.toString(args); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Model.java ================================================ package ognl.test.objects; /** * */ public class Model { public int getOptionCount() { return 1; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/MyMap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; import java.util.Map; /** * This tests the interface inheritence test. This is a subinterface * of Map and therefore should inherit the Map property accessor. */ public interface MyMap extends Map { public String getDescription(); } ================================================ FILE: ognl/src/test/java/ognl/test/objects/MyMapImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * This tests the interface inheritence test. This test implements * MyMap->Map but extends Object, therefore should be coded using * MapPropertyAccessor instead of ObjectPropertyAccessor. */ public class MyMapImpl extends Object implements MyMap { private Map map = new HashMap(); public void clear() { map.clear(); } public boolean containsKey(Object key) { return map.containsKey(key); } public boolean containsValue(Object value) { return map.containsValue(value); } public Set entrySet() { return map.entrySet(); } public boolean equals(Object o) { return map.equals(o); } public Object get(Object key) { return map.get(key); } public int hashCode() { return map.hashCode(); } public boolean isEmpty() { return map.isEmpty(); } public Set keySet() { return map.keySet(); } public Object put(Object key, Object value) { return map.put(key, value); } public void putAll(Map t) { map.putAll(t); } public Object remove(Object key) { return map.remove(key); } public int size() { return map.size(); } public Collection values() { return map.values(); } public String getDescription() { return "MyMap implementation"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/ObjectIndexed.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; public class ObjectIndexed extends BaseObjectIndexed { public ObjectIndexed() { super(); setAttribute("foo", "bar"); setAttribute("bar", "baz"); setAttribute("other", new OtherObjectIndexed()); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/OtherEnum.java ================================================ package ognl.test.objects; /** * */ public enum OtherEnum { ONE(1); public static final String STATIC_STRING = "string"; private int _value; private OtherEnum(int value) { _value = value; } public int getValue() { return _value; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/OtherObjectIndexed.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; public class OtherObjectIndexed extends BaseObjectIndexed { public OtherObjectIndexed() { super(); setAttribute("foo", "bar"); setAttribute("bar", "baz"); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/PersonGenericObject.java ================================================ package ognl.test.objects; /** * */ public class PersonGenericObject implements GenericObject { public int getId() { return 1; } public String getDisplayName() { return "Henry Collins"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/PropertyHolder.java ================================================ package ognl.test.objects; /** * Simple class used to test various kind of property resolutions. */ public class PropertyHolder { String _value = ""; String _search = "foo"; public String getValue() { return _value; } public void setValue(String value) { _value = value; } public boolean hasValue() { return _value != null && _value.length() > 0; } public void setSearch(String value) { _search = value; } public String getSearch() { return _search; } public void search() { } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Root.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; import ognl.DynamicSubscript; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; public class Root { public static final String SIZE_STRING = "size"; public static final int STATIC_INT = 23; private int[] array = {1, 2, 3, 4}; private final Map map = new HashMap<>(23); private final MyMap myMap = new MyMapImpl(); private final List list = Arrays.asList(null, this, array); private final List settableList = new ArrayList<>(Arrays.asList("foo", "bar", "baz")); private final int index = 1; private int intValue = 0; private String stringValue; private int yetAnotherIntValue = 46; private boolean privateAccessorBooleanValue = true; private int privateAccessorIntValue = 67; private int privateAccessorIntValue2 = 67; private int privateAccessorIntValue3 = 67; public String anotherStringValue = "foo"; public int anotherIntValue = 123; public int six = 6; private boolean _disabled; private Locale _selected = Locale.getDefault(); private final List> _booleanValues = new ArrayList<>(); private final boolean[] _booleanArray = {true, false, true, true}; private List _list; private final int verbosity = 87; private final BeanProvider _beanProvider = new BeanProviderImpl(); private boolean _render; private Boolean _readOnly = Boolean.FALSE; private final Integer _objIndex = 1; private final Object _genericObjIndex = 2; private final Date _date = new Date(); private boolean _openWindow = false; private final ITreeContentProvider _contentProvider = new TreeContentProvider(); private final Indexed _indexed = new Indexed(); private SearchTab _tab = new SearchTab(); /*=================================================================== Public static methods ===================================================================*/ public static int getStaticInt() { return STATIC_INT; } /*=================================================================== Constructors ===================================================================*/ public Root() { map.put("test", this); map.put("array", array); map.put("list", list); map.put("size", 5000); map.put(DynamicSubscript.first, 99); map.put("baz", array); map.put("value", new Bean2()); map.put("bar", new Bean3()); map.put(82L, "StringStuff=someValue"); IFormComponent comp = new FormComponentImpl(); comp.setClientId("formComponent"); IForm form = new FormImpl(); form.setClientId("form1"); comp.setForm(form); map.put("comp", comp); Map newMap = new HashMap(); Map chain = new HashMap(); newMap.put("deep", chain); chain.put("last", Boolean.TRUE); map.put("nested", newMap); /* make myMap identical */ myMap.putAll(map); List bool1 = new ArrayList(); bool1.add(Boolean.TRUE); bool1.add(Boolean.FALSE); bool1.add(Boolean.TRUE); _booleanValues.add(bool1); List bool2 = new ArrayList(); bool2.add(Boolean.TRUE); bool2.add(Boolean.FALSE); bool2.add(Boolean.TRUE); _booleanValues.add(bool2); } private boolean isPrivateAccessorBooleanValue() { return privateAccessorBooleanValue; } private void setPrivateAccessorBooleanValue(boolean value) { privateAccessorBooleanValue = value; } private int getPrivateAccessorIntValue() { return privateAccessorIntValue; } private void setPrivateAccessorIntValue(int value) { privateAccessorIntValue = value; } /*=================================================================== Protected methods ===================================================================*/ protected int getPrivateAccessorIntValue2() { return privateAccessorIntValue2; } protected void setPrivateAccessorIntValue2(int value) { privateAccessorIntValue2 = value; } /*=================================================================== Package protected methods ===================================================================*/ int getPrivateAccessorIntValue3() { return privateAccessorIntValue3; } void setPrivateAccessorIntValue3(int value) { privateAccessorIntValue3 = value; } /*=================================================================== Public methods ===================================================================*/ public int[] getArray() { return array; } public boolean[] getBooleanArray() { return _booleanArray; } public void setArray(int[] value) { array = value; } public String format(String key, Object value) { return format(key, new Object[]{value}); } public String format(String key, Object[] value) { return "formatted: " + key + " " + Arrays.toString(value); } public String getCurrentClass(String value) { return value + " stop"; } public Messages getMessages() { return new Messages(map); } public Map getMap() { return map; } public MyMap getMyMap() { return myMap; } public List getList() { return list; } public Object getAsset(String key) { return key; } public List getSettableList() { return settableList; } public int getIndex() { return index; } public Integer getObjectIndex() { return _objIndex; } public Integer getNullIndex() { return null; } public Object getGenericIndex() { return _genericObjIndex; } public int getIntValue() { return intValue; } public void setIntValue(int value) { intValue = value; } public int getTheInt() { return six; } public String getStringValue() { return stringValue; } public void setStringValue(String value) { stringValue = value; } public String getIndexedStringValue() { return "array"; } public Object getNullObject() { return null; } public String getTestString() { return "wiggle"; } public Object getProperty() { return new Bean2(); } public Bean2 getBean2() { return new Bean2(); } public Object getIndexedProperty(String name) { return myMap.get(name); } public Indexed getIndexer() { return _indexed; } public BeanProvider getBeans() { return _beanProvider; } public boolean getBooleanValue() { return _disabled; } public void setBooleanValue(boolean value) { _disabled = value; } public boolean getDisabled() { return _disabled; } public void setDisabled(boolean disabled) { _disabled = disabled; } public Locale getSelected() { return _selected; } public void setSelected(Locale locale) { _selected = locale; } public Locale getCurrLocale() { return Locale.getDefault(); } public int getCurrentLocaleVerbosity() { return verbosity; } public boolean getRenderNavigation() { return _render; } public void setSelectedList(List selected) { _list = selected; } public List getSelectedList() { return _list; } public Boolean getReadonly() { return _readOnly; } public void setReadonly(Boolean value) { _readOnly = value; } public Object getSelf() { return this; } public Date getTestDate() { return _date; } public String getWidth() { return "238px"; } public Long getTheLong() { return 4L; } public boolean isSorted() { return true; } public TestClass getMyTest() { return new TestImpl(); } public ITreeContentProvider getContentProvider() { return _contentProvider; } public boolean isPrintDelivery() { return true; } public Long getCurrentDeliveryId() { return 1l; } public Boolean isFlyingMonkey() { return Boolean.TRUE; } public Boolean isDumb() { return Boolean.FALSE; } public Date getExpiration() { return null; } public Long getMapKey() { return 82L; } public Object getArrayValue() { return new Object[]{Integer.valueOf("2"), Integer.valueOf("2")}; } public List getResult() { List list = new ArrayList<>(); list.add(new Object[]{Integer.valueOf("2"), Integer.valueOf("2")}); list.add(new Object[]{Integer.valueOf("2"), Integer.valueOf("2")}); list.add(new Object[]{Integer.valueOf("2"), Integer.valueOf("2")}); return list; } public boolean isEditorDisabled() { return false; } public boolean isDisabled() { return true; } public boolean isOpenTransitionWin() { return _openWindow; } public void setOpenTransitionWin(boolean value) { _openWindow = value; } public boolean isOk(SimpleEnum value, String otherValue) { return true; } public List> getBooleanValues() { return _booleanValues; } public int getIndex1() { return 1; } public int getIndex2() { return 1; } public SearchTab getTab() { return _tab; } public void setTab(SearchTab tab) { _tab = tab; } public static class A { public int methodOfA(B b) { return 0; } public int getIntValue() { return 1; } } public static class B { public int methodOfB(int i) { return 0; } } public A getA() { return new A(); } public B getB() { return new B(); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/SearchCriteria.java ================================================ package ognl.test.objects; /** * Test for OGNL-131. */ public class SearchCriteria { String _displayName; public SearchCriteria(String name) { _displayName = name; } public String getDisplayName() { return _displayName; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/SearchTab.java ================================================ package ognl.test.objects; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Test for OGNL-131. */ public class SearchTab { /** * Flags stating which search criteria are selected */ private List> searchCriteriaSelections = new ArrayList>(); { searchCriteriaSelections.add(Arrays.asList(Boolean.TRUE, Boolean.FALSE, Boolean.FALSE)); searchCriteriaSelections.add(Arrays.asList(Boolean.FALSE, Boolean.TRUE, Boolean.TRUE)); } public List> getSearchCriteriaSelections() { return this.searchCriteriaSelections; } public void setSearchCriteriaSelections(List> selections) { this.searchCriteriaSelections = selections; } /** * Filters that can be applied to this tabs searches */ private List searchCriteria = new ArrayList(); { searchCriteria.add(new SearchCriteria("Crittery critters")); searchCriteria.add(new SearchCriteria("Woodland creatures")); } public List getSearchCriteria() { return this.searchCriteria; } public void setSearchCriteria(List searchCriteria) { this.searchCriteria = searchCriteria; } /** * 2D list of options available for each criteria */ private List> searchCriteriaOptions = new ArrayList>(); public List> getSearchCriteriaOptions() { return this.searchCriteriaOptions; } public void setSearchCriteriaOptions(List> searchCriteriaOptions) { this.searchCriteriaOptions = searchCriteriaOptions; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/SecondBean.java ================================================ /** * */ package ognl.test.objects; /** * */ public class SecondBean extends BaseBean { public String getName() { return "SecondBean"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/SetterReturns.java ================================================ package ognl.test.objects; /** * */ public class SetterReturns { private String _value = ""; public String getValue() { return _value; } public SetterReturns setValue(String value) { _value += value; return this; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Simple.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.objects; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; import java.util.Objects; public class Simple { private String stringValue = "test"; private float floatValue; private int intValue; private boolean booleanValue; private BigInteger bigIntValue = BigInteger.valueOf(0); private BigDecimal bigDecValue = new BigDecimal(0.0); private Root root = new Root(); private Bean3 _bean; private Bean2 _bean2; private Object[] _array; private Messages _messages; private Object[] values; public Simple() { Map src = new HashMap(); src.put("test", "This is a test"); _messages = new Messages(src); } public Simple(Bean3 bean) { _bean = bean; } public Simple(Bean2 bean) { _bean2 = bean; } public Simple(Object[] values) { this.values = values; } public Simple(String stringValue, float floatValue, int intValue) { super(); this.stringValue = stringValue; this.floatValue = floatValue; this.intValue = intValue; } public void setValues(String stringValue, float floatValue, int intValue) { this.stringValue = stringValue; this.floatValue = floatValue; this.intValue = intValue; } public String getStringValue() { return stringValue; } public void setStringValue(String value) { stringValue = value; } public float getFloatValue() { return floatValue; } public void setFloatValue(float value) { floatValue = value; } public int getIntValue() { return intValue; } public void setIntValue(int value) { intValue = value; } public boolean getValueIsTrue(Object currValue) { return true; } public boolean getBooleanValue() { return booleanValue; } public void setBooleanValue(boolean value) { booleanValue = value; } public BigInteger getBigIntValue() { return bigIntValue; } public void setArray(Object[] values) { _array = values; } public Object[] getArray() { return _array; } public void setBigIntValue(BigInteger value) { bigIntValue = value; } public BigDecimal getBigDecValue() { return bigDecValue; } public void setBigDecValue(BigDecimal value) { bigDecValue = value; } public Root getRootValue() { return root; } public MethodTestMethods getTestMethods() { return new MethodTestMethods(); } public Messages getMessages() { return _messages; } public int getOne() { return 1; } public int getTwo() { return 2; } public int getThree() { return 3; } public int getTestValue(int val) { return val + 1; } public boolean isEditorDisabled() { return false; } public boolean isDisabled() { return true; } public boolean getIsTruck() { return true; } public GetterMethods getMethodsTest() { return new GetterMethods(); } public String getDisplayValue(int val) { return "test"; } public Object[] getValues() { return values; } public boolean equals(Object other) { boolean result = false; if (other instanceof Simple os) { result = Objects.equals(os.getStringValue(), getStringValue()) && (os.getIntValue() == getIntValue()); } return result; } public boolean isThisVarArgsWorking(Object... arguments) { return true; } public String isNullVarArgs() { return "null"; } public String isStringVarArgs(String... arguments) { return "args"; } public TestInterface get() { return new TestInterface() { @Override public String request() { return "null"; } @Override public String request(Object... args) { return "args"; } }; } interface TestInterface { String request(); String request(Object... args); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/SimpleEnum.java ================================================ package ognl.test.objects; /** * */ public enum SimpleEnum { ONE(1); private int _value; private SimpleEnum(int value) { _value = value; } public int getValue() { return _value; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/SimpleNumeric.java ================================================ package ognl.test.objects; /** * Used for {@link ognl.test.PropertyArithmeticAndLogicalOperatorsTest}. */ public class SimpleNumeric { public double getBudget() { return 140; } public double getTimeBilled() { return 24.12; } public String getTableSize() { return "10"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/StaticInterface.java ================================================ package ognl.test.objects; public interface StaticInterface { static String staticMethod() { return "static"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/SubclassSyntheticObject.java ================================================ package ognl.test.objects; import java.util.ArrayList; /** * Simple subclass. */ public class SubclassSyntheticObject extends BaseSyntheticObject { public ArrayList getList() { return new ArrayList(); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/TestClass.java ================================================ package ognl.test.objects; /** * */ public abstract class TestClass { } ================================================ FILE: ognl/src/test/java/ognl/test/objects/TestImpl.java ================================================ package ognl.test.objects; import java.util.HashMap; import java.util.Map; /** * */ public class TestImpl extends TestClass { public Map getTheMap() { Map map = new HashMap(); map.put("key", "value"); return map; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/TestInherited1.java ================================================ package ognl.test.objects; /** * */ public class TestInherited1 implements Inherited { public String getMyString() { return "inherited1"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/TestInherited2.java ================================================ package ognl.test.objects; /** * */ public class TestInherited2 implements Inherited { public String getMyString() { return "inherited2"; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/TestModel.java ================================================ package ognl.test.objects; /** * */ public class TestModel { public Copy getCopy() { return new Copy(); } public Model getUnassignedCopyModel() { return new Model(); } public boolean isCanApproveCopy() { return true; } public Entry getEntry() { return new Entry(); } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/TreeContentProvider.java ================================================ package ognl.test.objects; import java.util.Collection; import java.util.Collections; import java.util.List; /** * */ public class TreeContentProvider implements ITreeContentProvider { public Collection getChildren(Object parentElement) { return Collections.EMPTY_LIST; } public boolean hasChildren(Object parentElement) { return true; } public List getElements() { return Collections.EMPTY_LIST; } } ================================================ FILE: ognl/src/test/java/ognl/test/objects/Two.java ================================================ package ognl.test.objects; /** * */ public class Two { public String getMessage(String mes) { return "[" + mes + "]"; } public boolean hasChildren(String name) { return name.length() > 2; } } ================================================ FILE: ognl/src/test/java/ognl/test/race/Base.java ================================================ package ognl.test.race; public class Base { private Boolean yn = true; public Boolean getYn() { return yn; } public void setYn(Boolean yn) { this.yn = yn; } } ================================================ FILE: ognl/src/test/java/ognl/test/race/Person.java ================================================ package ognl.test.race; public class Person extends Base { private String name = "abc"; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: ognl/src/test/java/ognl/test/race/RaceTest.java ================================================ package ognl.test.race; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; class RaceTest { @Test void testOgnlRace() { int concurrent = 128; final int batchCount = 2000; final CountDownLatch start = new CountDownLatch(1); final CountDownLatch wait = new CountDownLatch(concurrent); final AtomicInteger errCount = new AtomicInteger(0); final Person person = new Person(); for (int i = 0; i < concurrent; i++) { Thread thread = new Thread(() -> { try { start.await(); } catch (InterruptedException e) { // ignore } for (int j = 0; j < batchCount; j++) { if (j % 2 == 0) { runValue(person, "yn", errCount); } else { runValue(person, "name", errCount); } } wait.countDown(); }); thread.setName("work-" + i); thread.start(); } start.countDown(); try { wait.await(); } catch (InterruptedException e) { // ignore } assertEquals(0, errCount.get()); } private void runValue(Person person, String name, AtomicInteger errCount) { OgnlContext context = Ognl.createDefaultContext(person); try { Ognl.getValue(name, context); } catch (OgnlException e) { errCount.incrementAndGet(); // ignore } } } ================================================ FILE: ognl/src/test/java/ognl/test/util/ContextClassLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.util; import ognl.OgnlContext; public class ContextClassLoader extends ClassLoader { private OgnlContext context; /*=================================================================== Constructors ===================================================================*/ public ContextClassLoader(ClassLoader parentClassLoader, OgnlContext context) { super(parentClassLoader); this.context = context; } /*=================================================================== Overridden methods ===================================================================*/ protected Class findClass(String name) throws ClassNotFoundException { if ((context != null) && (context.getClassResolver() != null)) { return context.getClassResolver().classForName(name, context); } return super.findClass(name); } } ================================================ FILE: ognl/src/test/java/ognl/test/util/EnhancedClassLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.util; public class EnhancedClassLoader extends ClassLoader { /*=================================================================== Constructors ===================================================================*/ public EnhancedClassLoader(ClassLoader parentClassLoader) { super(parentClassLoader); } /*=================================================================== Overridden methods ===================================================================*/ public Class defineClass(String enhancedClassName, byte[] byteCode) { return defineClass(enhancedClassName, byteCode, 0, byteCode.length); } } ================================================ FILE: ognl/src/test/java/ognl/test/util/NameFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package ognl.test.util; public class NameFactory extends Object { private String classBaseName; private int classNameCounter = 0; private String variableBaseName; private int variableNameCounter = 0; /*=================================================================== Constructors ===================================================================*/ public NameFactory(String classBaseName, String variableBaseName) { super(); this.classBaseName = classBaseName; this.variableBaseName = variableBaseName; } /*=================================================================== Public methods ===================================================================*/ public String getNewClassName() { return classBaseName + classNameCounter++; } public String getNewVariableName() { return variableBaseName + variableNameCounter++; } } ================================================ FILE: ognl/src/test/java/sun/test/PublicTestInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package sun.test; /** * Public interface in sun.test package for testing. *

* Since this is an interface, isLikelyAccessible() will return true for it, * even though it's in a "sun." package. */ public interface PublicTestInterface { String testMethod(); } ================================================ FILE: ognl/src/test/java/sun/test/SimulatedInternalClass.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package sun.test; /** * This class is intentionally in the "sun.test" package to simulate * an internal JDK class for testing purposes. *

* The isLikelyAccessible() method will return false for classes in packages * starting with "sun.", which allows us to test the accessibility logic * without needing actual internal JDK classes. */ public class SimulatedInternalClass implements sun.test.PublicTestInterface { @Override public String testMethod() { return "internal"; } public String anotherMethod() { return "internal-another"; } } ================================================ FILE: pom.xml ================================================ 4.0.0 ognl ognl-parent pom 3.5.0-SNAPSHOT OGNL Parent OGNL - Parent module of Object Graph Navigation Library UTF-8 17 17 https://sonarcloud.io orphan-oss orphan-oss_ognl 3.31.0-GA 6.1.0 1.37 2.21.3 3.5.5 3.15.0 3.5.0 3.4.0 3.12.0 3.1.4 3.2.0 0.10.0 3.6.2 3.6.3 0.8.14 5.6.0.6792 3.3.1 3.2.8 2025 https://github.com/orphan-oss/ognl/orphan/ognl/ The Apache Software License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0.txt repo scm:git:git@github.com:orphan-oss/ognl.git git@github.com:orphan-oss/ognl.git scm:git:git@github.com:orphan-oss/ognl.git HEAD Github Issues https://github.com/orphan-oss/ognl/issues lukaszlenart lukasz.lenart@gmail.com Lead maintainer org.javassist javassist ${javassist.version} org.junit.jupiter junit-jupiter-engine ${junit.version} org.junit.jupiter junit-jupiter-params ${junit.version} org.openjdk.jmh jmh-core ${jmh.version} org.openjdk.jmh jmh-generator-annprocess ${jmh.version} com.fasterxml.jackson.core jackson-databind ${jackson-databind.version} ognl benchmarks org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} org.apache.maven.plugins maven-source-plugin ${maven-source-plugin.version} org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin.version} org.codehaus.mojo javacc-maven-plugin ${javacc-maven-plugin.version} org.apache.maven.plugins maven-release-plugin ${maven-release-plugin.version} org.sonatype.central central-publishing-maven-plugin ${central-publishing-maven-plugin.version} org.apache.maven.plugins maven-deploy-plugin ${maven-deploy-plugin.version} org.apache.maven.plugins maven-shade-plugin ${maven-shade-plugin.version} org.codehaus.mojo exec-maven-plugin ${exec-maven-plugin.version} org.jacoco jacoco-maven-plugin ${jacoco-maven-plugin.version} org.sonarsource.scanner.maven sonar-maven-plugin ${sonar-maven-plugin.version} org.apache.maven.plugins maven-compiler-plugin org.apache.maven.plugins maven-deploy-plugin true org.apache.maven.plugins maven-release-plugin true v@{project.version} release org.sonatype.central central-publishing-maven-plugin true central install coverage org.sonarsource.scanner.maven sonar-maven-plugin org.jacoco jacoco-maven-plugin report-aggregate verify report-aggregate release deploy maven-gpg-plugin org.apache.maven.plugins maven-gpg-plugin ${maven-gpg-plugin.version} sign-artifacts verify sign ================================================ FILE: renovate.json ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base" ], "baseBranches": ["main", "ognl-3-4-x"], "packageRules": [ { "matchUpdateTypes": ["major","minor", "patch"], "matchCurrentVersion": "!/^0/", "automerge": true } ] }