├── .dockerignore ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .project ├── .settings ├── com.wdev91.eclipse.copyright.xml ├── org.eclipse.core.resources.prefs ├── org.eclipse.core.runtime.prefs ├── org.eclipse.jdt.apt.core.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.jdt.ui.prefs ├── Dockerfile ├── Dockerfile.aarch64 ├── LICENSE.txt ├── README.md ├── build-jq.sh ├── build.gradle ├── core.gradle ├── docker-build.sh ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── c │ ├── jq.h │ └── jv.h ├── java │ └── com │ │ └── arakelian │ │ └── jq │ │ ├── JqLibrary.java │ │ ├── JqRequest.java │ │ ├── JqResponse.java │ │ ├── NativeLib.java │ │ └── package-info.java └── resources │ └── lib │ ├── darwin-aarch64 │ └── libjq.dylib │ ├── darwin-x86_64 │ └── libjq.dylib │ ├── linux-aarch64 │ └── libjq.so │ └── linux-x86_64 │ └── libjq.so └── test ├── java └── com │ └── arakelian │ └── jq │ ├── AbstractJqTest.java │ ├── Base64Test.java │ ├── JqTest.java │ ├── OnigurumaTest.java │ └── OptionalTest.java └── resources ├── base64.test ├── jq.test ├── logging.properties ├── modules ├── .jq ├── a.jq ├── b │ └── b.jq ├── c │ ├── c.jq │ └── d.jq ├── data.json ├── lib │ └── jq │ │ ├── e │ │ └── e.jq │ │ └── f.jq ├── syntaxerror │ └── syntaxerror.jq ├── test_bind_order.jq ├── test_bind_order0.jq ├── test_bind_order1.jq └── test_bind_order2.jq ├── onig.test └── optional.test /.dockerignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.gradle 3 | LICENSE.txt 4 | README.md 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | workflow_dispatch: 11 | branches: 12 | - main 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | java: [ '11' ] 20 | steps: 21 | - uses: actions/checkout@v2 22 | with: 23 | fetch-depth: 0 24 | - name: Set up JDK ${{ matrix.java }} 25 | uses: actions/setup-java@v2 26 | with: 27 | java-version: ${{ matrix.java }} 28 | distribution: 'adopt' 29 | - name: Print Java version 30 | run: java -version 31 | - name: Gradle wrapper validation 32 | uses: gradle/wrapper-validation-action@v1 33 | - name: Run tests 34 | run: ./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --stacktrace clean build 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | ### Emacs ### 5 | # -*- mode: gitignore; -*- 6 | *~ 7 | \#*\# 8 | .\#* 9 | 10 | ### Gradle ### 11 | .gradle 12 | build/ 13 | 14 | ### Node ### 15 | # Logs 16 | logs 17 | *.log 18 | npm-debug.log* 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Dependency directory 29 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 30 | node_modules 31 | 32 | 33 | ### Intellij ### 34 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 35 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 36 | 37 | # User-specific stuff: 38 | .idea/workspace.xml 39 | .idea/tasks.xml 40 | .idea/dictionaries 41 | .idea/vcs.xml 42 | .idea/jsLibraryMappings.xml 43 | 44 | # Sensitive or high-churn files: 45 | .idea/dataSources.ids 46 | .idea/dataSources.xml 47 | .idea/dataSources.local.xml 48 | .idea/sqlDataSources.xml 49 | .idea/dynamic.xml 50 | .idea/uiDesigner.xml 51 | 52 | # Gradle: 53 | .idea/gradle.xml 54 | .idea/libraries 55 | 56 | # Mongo Explorer plugin: 57 | .idea/mongoSettings.xml 58 | 59 | ## File-based project format: 60 | *.iws 61 | 62 | ## Plugin-specific files: 63 | 64 | # IntelliJ 65 | /out/ 66 | 67 | # mpeltonen/sbt-idea plugin 68 | .idea_modules/ 69 | 70 | # JIRA plugin 71 | atlassian-ide-plugin.xml 72 | 73 | # Crashlytics plugin (for Android Studio and IntelliJ) 74 | com_crashlytics_export_strings.xml 75 | crashlytics.properties 76 | crashlytics-build.properties 77 | fabric.properties 78 | 79 | ### Intellij Patch ### 80 | *.iml 81 | 82 | 83 | ### Eclipse ### 84 | .metadata 85 | .classpath 86 | .factorypath 87 | .apt_generated 88 | generated_src 89 | bin/ 90 | target/ 91 | tmp/ 92 | *.tmp 93 | *.bak 94 | *.swp 95 | *~.nib 96 | 97 | 98 | ### grunt ### 99 | # Grunt usually compiles files inside this directory 100 | dist/ 101 | 102 | # Grunt usually preprocesses files such as coffeescript, compass... inside the .tmp directory 103 | .tmp/ 104 | /.apt_generated_tests/ 105 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | java-jq 4 | Java JQ 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/com.wdev91.eclipse.copyright.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 |
49 |
50 |
51 | 52 | 53 | 54 |
55 | 56 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 -------------------------------------------------------------------------------- /.settings/org.eclipse.core.runtime.prefs: -------------------------------------------------------------------------------- 1 | #Sun Mar 21 12:59:37 EDT 2010 2 | eclipse.preferences.version=1 3 | line.separator=\n 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.apt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.apt.aptEnabled=true 3 | org.eclipse.jdt.apt.genSrcDir=target/generated_src 4 | org.eclipse.jdt.apt.reconcileEnabled=true 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled 3 | org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore 4 | org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull 5 | org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= 6 | org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault 7 | org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= 8 | org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable 9 | org.eclipse.jdt.core.compiler.annotation.nullable.secondary= 10 | org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled 11 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 12 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate 13 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 14 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 15 | org.eclipse.jdt.core.compiler.compliance=11 16 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 17 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 18 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 19 | org.eclipse.jdt.core.compiler.doc.comment.support=enabled 20 | org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning 21 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 22 | org.eclipse.jdt.core.compiler.problem.autoboxing=ignore 23 | org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning 24 | org.eclipse.jdt.core.compiler.problem.deadCode=ignore 25 | org.eclipse.jdt.core.compiler.problem.deprecation=warning 26 | org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled 27 | org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled 28 | org.eclipse.jdt.core.compiler.problem.discouragedReference=warning 29 | org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore 30 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 31 | org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore 32 | org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore 33 | org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled 34 | org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore 35 | org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning 36 | org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning 37 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 38 | org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning 39 | org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled 40 | org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning 41 | org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning 42 | org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore 43 | org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning 44 | org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled 45 | org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled 46 | org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled 47 | org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public 48 | org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore 49 | org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning 50 | org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore 51 | org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore 52 | org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled 53 | org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore 54 | org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore 55 | org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled 56 | org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public 57 | org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags 58 | org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore 59 | org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled 60 | org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled 61 | org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public 62 | org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore 63 | org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled 64 | org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore 65 | org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning 66 | org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning 67 | org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning 68 | org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore 69 | org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning 70 | org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning 71 | org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error 72 | org.eclipse.jdt.core.compiler.problem.nullReference=warning 73 | org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error 74 | org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning 75 | org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning 76 | org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore 77 | org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning 78 | org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore 79 | org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore 80 | org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore 81 | org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore 82 | org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning 83 | org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore 84 | org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore 85 | org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore 86 | org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore 87 | org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore 88 | org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled 89 | org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning 90 | org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled 91 | org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled 92 | org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled 93 | org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore 94 | org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning 95 | org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled 96 | org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning 97 | org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning 98 | org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore 99 | org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore 100 | org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore 101 | org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning 102 | org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore 103 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning 104 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled 105 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled 106 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled 107 | org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore 108 | org.eclipse.jdt.core.compiler.problem.unusedImport=warning 109 | org.eclipse.jdt.core.compiler.problem.unusedLabel=warning 110 | org.eclipse.jdt.core.compiler.problem.unusedLocal=warning 111 | org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning 112 | org.eclipse.jdt.core.compiler.problem.unusedParameter=warning 113 | org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled 114 | org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled 115 | org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled 116 | org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning 117 | org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore 118 | org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning 119 | org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning 120 | org.eclipse.jdt.core.compiler.processAnnotations=enabled 121 | org.eclipse.jdt.core.compiler.source=11 122 | org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 123 | org.eclipse.jdt.core.formatter.align_type_members_on_columns=false 124 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 125 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 126 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 127 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 128 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=48 129 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 130 | org.eclipse.jdt.core.formatter.alignment_for_assignment=0 131 | org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 132 | org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 133 | org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 134 | org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 135 | org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 136 | org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 137 | org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 138 | org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 139 | org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 140 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=48 141 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=48 142 | org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 143 | org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 144 | org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 145 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 146 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 147 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 148 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 149 | org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 150 | org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 151 | org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 152 | org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 153 | org.eclipse.jdt.core.formatter.blank_lines_after_package=1 154 | org.eclipse.jdt.core.formatter.blank_lines_before_field=0 155 | org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 156 | org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 157 | org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 158 | org.eclipse.jdt.core.formatter.blank_lines_before_method=1 159 | org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 160 | org.eclipse.jdt.core.formatter.blank_lines_before_package=0 161 | org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 162 | org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 163 | org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line 164 | org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line 165 | org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line 166 | org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line 167 | org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line 168 | org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line 169 | org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line 170 | org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line 171 | org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line 172 | org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line 173 | org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line 174 | org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line 175 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true 176 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false 177 | org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false 178 | org.eclipse.jdt.core.formatter.comment.format_block_comments=true 179 | org.eclipse.jdt.core.formatter.comment.format_header=false 180 | org.eclipse.jdt.core.formatter.comment.format_html=true 181 | org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true 182 | org.eclipse.jdt.core.formatter.comment.format_line_comments=true 183 | org.eclipse.jdt.core.formatter.comment.format_source_code=true 184 | org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true 185 | org.eclipse.jdt.core.formatter.comment.indent_root_tags=true 186 | org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert 187 | org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert 188 | org.eclipse.jdt.core.formatter.comment.line_length=100 189 | org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true 190 | org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true 191 | org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false 192 | org.eclipse.jdt.core.formatter.compact_else_if=true 193 | org.eclipse.jdt.core.formatter.continuation_indentation=2 194 | org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 195 | org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off 196 | org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on 197 | org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false 198 | org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true 199 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true 200 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true 201 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true 202 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true 203 | org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true 204 | org.eclipse.jdt.core.formatter.indent_empty_lines=false 205 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true 206 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true 207 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true 208 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false 209 | org.eclipse.jdt.core.formatter.indentation.size=4 210 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert 211 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert 212 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert 213 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert 214 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert 215 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert 216 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert 217 | org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert 218 | org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert 219 | org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert 220 | org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert 221 | org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert 222 | org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert 223 | org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert 224 | org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert 225 | org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert 226 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert 227 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert 228 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert 229 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert 230 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert 231 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert 232 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert 233 | org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert 234 | org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert 235 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert 236 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert 237 | org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert 238 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert 239 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert 240 | org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert 241 | org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert 242 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert 243 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert 244 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert 245 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert 246 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert 247 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert 248 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert 249 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert 250 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert 251 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert 252 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert 253 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert 254 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert 255 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert 256 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert 257 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert 258 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert 259 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert 260 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert 261 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert 262 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert 263 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert 264 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert 265 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert 266 | org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert 267 | org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert 268 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert 269 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert 270 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert 271 | org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert 272 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert 273 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert 274 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert 275 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert 276 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert 277 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert 278 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert 279 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert 280 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert 281 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert 282 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert 283 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert 284 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert 285 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert 286 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert 287 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert 288 | org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert 289 | org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert 290 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert 291 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert 292 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert 293 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert 294 | org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert 295 | org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert 296 | org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert 297 | org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert 298 | org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert 299 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert 300 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert 301 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert 302 | org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert 303 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert 304 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert 305 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert 306 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert 307 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert 308 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert 309 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert 310 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert 311 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert 312 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert 313 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert 314 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert 315 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert 316 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert 317 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert 318 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert 319 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert 320 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert 321 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert 322 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert 323 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert 324 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert 325 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert 326 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert 327 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert 328 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert 329 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert 330 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert 331 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert 332 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert 333 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert 334 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert 335 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert 336 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert 337 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert 338 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert 339 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert 340 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert 341 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert 342 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert 343 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert 344 | org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert 345 | org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert 346 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert 347 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert 348 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert 349 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert 350 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert 351 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert 352 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert 353 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert 354 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert 355 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert 356 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert 357 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert 358 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert 359 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert 360 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert 361 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert 362 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert 363 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert 364 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert 365 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert 366 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert 367 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert 368 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert 369 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert 370 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert 371 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert 372 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert 373 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert 374 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert 375 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert 376 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert 377 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert 378 | org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert 379 | org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert 380 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert 381 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert 382 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert 383 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert 384 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert 385 | org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert 386 | org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert 387 | org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert 388 | org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert 389 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert 390 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert 391 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert 392 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert 393 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert 394 | org.eclipse.jdt.core.formatter.join_lines_in_comments=true 395 | org.eclipse.jdt.core.formatter.join_wrapped_lines=true 396 | org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false 397 | org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false 398 | org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false 399 | org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false 400 | org.eclipse.jdt.core.formatter.lineSplit=110 401 | org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false 402 | org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false 403 | org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 404 | org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 405 | org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines 406 | org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines 407 | org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines 408 | org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines 409 | org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines 410 | org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines 411 | org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines 412 | org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines 413 | org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines 414 | org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines 415 | org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true 416 | org.eclipse.jdt.core.formatter.tabulation.char=space 417 | org.eclipse.jdt.core.formatter.tabulation.size=4 418 | org.eclipse.jdt.core.formatter.use_on_off_tags=true 419 | org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false 420 | org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false 421 | org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true 422 | org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true 423 | org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true 424 | org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true 425 | org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter 426 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | cleanup.add_default_serial_version_id=true 2 | cleanup.add_generated_serial_version_id=false 3 | cleanup.add_missing_annotations=true 4 | cleanup.add_missing_deprecated_annotations=true 5 | cleanup.add_missing_methods=false 6 | cleanup.add_missing_nls_tags=false 7 | cleanup.add_missing_override_annotations=true 8 | cleanup.add_missing_override_annotations_interface_methods=true 9 | cleanup.add_serial_version_id=false 10 | cleanup.always_use_blocks=true 11 | cleanup.always_use_parentheses_in_expressions=false 12 | cleanup.always_use_this_for_non_static_field_access=false 13 | cleanup.always_use_this_for_non_static_method_access=false 14 | cleanup.convert_functional_interfaces=false 15 | cleanup.convert_to_enhanced_for_loop=false 16 | cleanup.correct_indentation=true 17 | cleanup.format_source_code=true 18 | cleanup.format_source_code_changes_only=false 19 | cleanup.insert_inferred_type_arguments=false 20 | cleanup.make_local_variable_final=true 21 | cleanup.make_parameters_final=true 22 | cleanup.make_private_fields_final=true 23 | cleanup.make_type_abstract_if_missing_method=false 24 | cleanup.make_variable_declarations_final=true 25 | cleanup.never_use_blocks=false 26 | cleanup.never_use_parentheses_in_expressions=true 27 | cleanup.organize_imports=true 28 | cleanup.qualify_static_field_accesses_with_declaring_class=false 29 | cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true 30 | cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=false 31 | cleanup.qualify_static_member_accesses_with_declaring_class=true 32 | cleanup.qualify_static_method_accesses_with_declaring_class=false 33 | cleanup.remove_private_constructors=true 34 | cleanup.remove_redundant_type_arguments=true 35 | cleanup.remove_trailing_whitespaces=true 36 | cleanup.remove_trailing_whitespaces_all=true 37 | cleanup.remove_trailing_whitespaces_ignore_empty=false 38 | cleanup.remove_unnecessary_casts=true 39 | cleanup.remove_unnecessary_nls_tags=true 40 | cleanup.remove_unused_imports=true 41 | cleanup.remove_unused_local_variables=true 42 | cleanup.remove_unused_private_fields=true 43 | cleanup.remove_unused_private_members=false 44 | cleanup.remove_unused_private_methods=true 45 | cleanup.remove_unused_private_types=true 46 | cleanup.sort_members=true 47 | cleanup.sort_members_all=false 48 | cleanup.use_anonymous_class_creation=false 49 | cleanup.use_blocks=true 50 | cleanup.use_blocks_only_for_return_and_throw=false 51 | cleanup.use_lambda=true 52 | cleanup.use_parentheses_in_expressions=true 53 | cleanup.use_this_for_non_static_field_access=false 54 | cleanup.use_this_for_non_static_field_access_only_if_necessary=true 55 | cleanup.use_this_for_non_static_method_access=false 56 | cleanup.use_this_for_non_static_method_access_only_if_necessary=true 57 | cleanup.use_type_arguments=false 58 | cleanup_profile=_Arakelian Software profile 59 | cleanup_settings_version=2 60 | eclipse.preferences.version=1 61 | formatter_profile=_Arakelian Software profile 62 | formatter_settings_version=13 63 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # java-jq 2 | 3 | FROM ubuntu:14.04 4 | LABEL maintainer="Greg Arakelian " 5 | 6 | # install required software 7 | RUN apt update && \ 8 | apt install -y build-essential git autoconf automake libtool wget bash valgrind 9 | 10 | # copy script 11 | COPY build-jq.sh /usr/local/bin 12 | 13 | # build jq 14 | RUN cd ~ && \ 15 | chmod 755 /usr/local/bin/*.sh && \ 16 | build-jq.sh 17 | -------------------------------------------------------------------------------- /Dockerfile.aarch64: -------------------------------------------------------------------------------- 1 | FROM debian:buster-slim 2 | LABEL "description"="Docker image for building libjq" 3 | LABEL "author"="nicolae.natea" 4 | 5 | WORKDIR /home 6 | RUN apt-get update && apt-get install -y build-essential autoconf automake libtool curl 7 | ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-1.6.tar.gz . 8 | ADD https://raw.githubusercontent.com/Homebrew/formula-patches/03cf8088210822aa2c1ab544ed58ea04c897d9c4/libtool/configure-big_sur.diff flat_namespace.patch 9 | RUN tar --extract --file "jq-1.6.tar.gz" && \ 10 | cd /home/jq-1.6/modules/oniguruma && \ 11 | autoreconf -fi && \ 12 | cd /home/jq-1.6 && \ 13 | patch < /home/flat_namespace.patch && \ 14 | autoreconf -fi && \ 15 | CPPFLAGS="-D_REENTRANT -fPIC" ./configure --prefix="$INSTALL_BASE" --disable-maintainer-mode --disable-docs --with-oniguruma=builtin && \ 16 | make -j4 17 | RUN gcc -shared -o libjq.so -Wl,--whole-archive jq-1.6/modules/oniguruma/src/.libs/libonig.a jq-1.6/.libs/libjq.a -Wl,--no-whole-archive 18 | 19 | # Option to make all static: make LDFLAGS=-all-static 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Bradley Skaggs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-jq 2 | [![version](https://img.shields.io/maven-metadata/v.svg?label=release&metadataUrl=https://repo1.maven.org/maven2/com/arakelian/java-jq/maven-metadata.xml)](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.arakelian%22%20AND%20a%3A%22java-jq%22) 3 | [![CI](https://github.com/arakelian/java-jq/actions/workflows/ci.yml/badge.svg)](https://github.com/arakelian/java-jq/actions/workflows/ci.yml) 4 | 5 | java-jq is not a re-implementation of [jq](http://stedolan.github.io/jq/) in Java; instead, 6 | it embeds the necessary jq and Oniguruma native libraries in a jar file, and then uses 7 | [Java Native Access](https://github.com/java-native-access/jna) (JNA) to call the embedded 8 | libraries in a Java-friendly way. 9 | 10 | The distribution of java-jq includes native JQ 1.6 libraries for all major platforms (Mac, Windows and Linux), 11 | and includes a statically linked version of Oniguruma 5.9.6 to avoid any runtime compatibility issues. 12 | 13 | java-jq was heavily inspired by [jjq](https://github.com/bskaggs/jjq). 14 | 15 | 16 | ## Usage 17 | 18 | Using Java-JQ is very easy. 19 | 20 | 21 | First, let's get a reference to the Native JQ library. This class is a thread-safe singleton. 22 | 23 | ```java 24 | JqLibrary library = ImmutableJqLibrary.of(); 25 | ``` 26 | 27 | Now, let's create a JQ request. A "request" is an immutable bean that contains three basic elements: a reference 28 | to the JQ library we created above, the JSON input you want to transform, and the JQ filter expression that you 29 | want to execute. 30 | 31 | ```java 32 | final JqRequest request = ImmutableJqRequest.builder() // 33 | .lib(library) // 34 | .input("{\"a\":[1,2,3,4,5],\"b\":\"hello\"}") // 35 | .filter(".") // 36 | .build(); 37 | ``` 38 | 39 | As a final step, let's execute the request. 40 | 41 | ```java 42 | final JqResponse response = request.execute(); 43 | if( response.hasErrors() ) { 44 | // display errors in response.getErrors() 45 | } else { 46 | System.out.println( "JQ output: " + response.getOutput()); 47 | } 48 | ``` 49 | 50 | ## Compatibility 51 | 52 | As of version 1.1.0, java-jq successfully executes the complete [jq](http://stedolan.github.io/jq/) 53 | test suite, including all tests in jq.test, onig.test, base64.test, and optional.test. 54 | 55 | java-jq supports modules as well. To use modules, include the directory paths where your modules 56 | can be found with your JqRequest as follows: 57 | 58 | ```java 59 | final JqRequest request = ImmutableJqRequest.builder() // 60 | .lib(library) // 61 | .input("your json goes here") // 62 | .filter(".") // 63 | .addModulePath(new File("/some/modules/can/be/found/here")) // 64 | .addModulePath(new File("/other/modules/can/be/found/here")) // 65 | .build(); 66 | ``` 67 | 68 | 69 | ## Installation 70 | 71 | The library is available on [Maven Central](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.arakelian%22%20AND%20a%3A%22java-jq%22). 72 | 73 | ### Maven 74 | 75 | Add the following to your `pom.xml`: 76 | 77 | ```xml 78 | 79 | 80 | central 81 | Central Repository 82 | http://repo.maven.apache.org/maven2 83 | 84 | true 85 | 86 | 87 | 88 | 89 | ... 90 | 91 | 92 | com.arakelian 93 | java-jq 94 | 2.0.0 95 | test 96 | 97 | ``` 98 | 99 | ### Gradle 100 | 101 | Add the following to your `build.gradle`: 102 | 103 | ```groovy 104 | repositories { 105 | mavenCentral() 106 | } 107 | 108 | dependencies { 109 | testCompile 'com.arakelian:java-jq:2.0.0' 110 | } 111 | ``` 112 | 113 | ## Licence 114 | 115 | Apache Version 2.0 116 | -------------------------------------------------------------------------------- /build-jq.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p build 4 | cd build 5 | 6 | ONIGURUMA_VERSION=5.9.6 7 | JQ_VERSION=1.6 8 | 9 | PLATFORM=$(printf "$(uname)-$(uname -m)" | tr "[A-Z]" "[a-z]") 10 | 11 | wget https://github.com/kkos/oniguruma/releases/download/v${ONIGURUMA_VERSION}/onig-${ONIGURUMA_VERSION}.tar.gz && \ 12 | tar xvf onig-${ONIGURUMA_VERSION}.tar.gz && \ 13 | rm onig-${ONIGURUMA_VERSION}.tar.gz && \ 14 | cd onig-${ONIGURUMA_VERSION} && \ 15 | ./configure --prefix $(cd .. && pwd -P) && \ 16 | make && \ 17 | make install && \ 18 | cd .. 19 | 20 | wget https://github.com/stedolan/jq/releases/download/jq-${JQ_VERSION}/jq-${JQ_VERSION}.tar.gz && \ 21 | tar xvf jq-${JQ_VERSION}.tar.gz && \ 22 | rm jq-${JQ_VERSION}.tar.gz && \ 23 | cd jq-${JQ_VERSION} && \ 24 | ./configure --disable-maintainer-mode --prefix $(cd .. && pwd -P) --with-oniguruma=$(cd .. && pwd -P) --disable-docs && \ 25 | sed -i.bak 's/LIBS = -lonig/LIBS = /' Makefile && \ 26 | sed -i.bak "s/libjq_la_LIBADD = -lm/libjq_la_LIBADD = -lm $(find ../onig-${ONIGURUMA_VERSION} -name '*.lo' | xargs echo | sed 's/\//\\\//g')/" Makefile && \ 27 | sed -i.bak "s/jq_LDADD = libjq.la -lm/jq_LDADD = libjq.la -lm $(find ../onig-${ONIGURUMA_VERSION} -name '*.lo' | xargs echo | sed 's/\//\\\//g')/" Makefile && \ 28 | make && \ 29 | make install && \ 30 | cd .. 31 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // java-jq 2 | 3 | plugins { 4 | id 'java-library' 5 | id 'maven-publish' 6 | id 'signing' 7 | id 'eclipse' 8 | id 'idea' 9 | 10 | // keep dependencies up-to-date! 11 | id 'com.github.ben-manes.versions' version '0.51.0' 12 | 13 | // useful for creating immutable java beans 14 | id 'org.inferred.processors' version '3.7.0' 15 | 16 | // to ensure clean code 17 | id "net.ltgt.errorprone" version "3.1.0" 18 | 19 | // for deployment to Maven Central 20 | id "io.codearte.nexus-staging" version "0.30.0" 21 | } 22 | 23 | group = 'com.arakelian' 24 | version = '2.0.0' 25 | 26 | 27 | apply from: "core.gradle" 28 | 29 | wrapper { 30 | gradleVersion = '8.5' 31 | } 32 | 33 | publishing.publications.mavenJava { 34 | pom { 35 | name = "Java JQ" 36 | description = "java-jq is not a re-implementation of jq in Java. It uses JNA to access embedded libraries." 37 | url = "https://github.com/arakelian/java-jq" 38 | 39 | licenses { 40 | license { 41 | name = 'The Apache License, Version 2.0' 42 | url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 43 | } 44 | } 45 | 46 | developers { 47 | developer { 48 | id = 'arakelian' 49 | name = 'Greg Arakelian' 50 | email = 'greg@arakelian.com' 51 | } 52 | } 53 | 54 | scm { 55 | connection = 'scm:git:https://github.com/arakelian/java-jq.git' 56 | developerConnection = 'scm:git:git@github.com:arakelian/java-jq.git' 57 | url = 'https://github.com/arakelian/java-jq.git' 58 | } 59 | } 60 | } 61 | 62 | dependencies { 63 | processor 'org.immutables:value:2.10.1' 64 | 65 | // annotations 66 | api 'org.immutables:value-annotations:2.10.1' 67 | 68 | // configure errorprone version 69 | errorprone 'com.google.errorprone:error_prone_core:2.27.0' 70 | 71 | // miscellaneous usages 72 | api 'com.google.guava:guava:33.1.0-jre' 73 | 74 | // needed for access to native code 75 | api 'net.java.dev.jna:jna:5.14.0' 76 | 77 | // logging 78 | testImplementation 'org.apache.logging.log4j:log4j-api:2.21.1' 79 | testImplementation 'org.apache.logging.log4j:log4j-core:2.21.1' 80 | testImplementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.21.1' 81 | testImplementation 'org.slf4j:jcl-over-slf4j:2.0.12' 82 | testImplementation 'org.slf4j:jul-to-slf4j:2.0.12' 83 | api 'org.slf4j:slf4j-api:2.0.12' 84 | 85 | // for unit testing 86 | testImplementation 'com.fasterxml.jackson.core:jackson-core:2.17.0' 87 | testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0' 88 | testImplementation 'com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.0' 89 | testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.0' 90 | testImplementation 'net.javacrumbs.json-unit:json-unit:2.38.0' // last for JDK 11 91 | 92 | // for unit testing 93 | testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' 94 | } 95 | -------------------------------------------------------------------------------- /core.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | repositories { 4 | mavenCentral() 5 | } 6 | dependencies { 7 | classpath 'com.guardsquare:proguard-gradle:7.2.1' // The ProGuard Gradle plugin. 8 | } 9 | } 10 | 11 | 12 | ext { 13 | // useful macros, you can add your own 14 | macros = [ 15 | 'all' : [ 16 | 'clean', 17 | 'classpath', 18 | 'build', 19 | 'minify', 20 | 'generatePomFileForInternalPublication', // needed to generate internal POM 21 | 'publishMavenJavaPublicationToMavenLocal' // real publication to Maven Local (~/.m2/repository) 22 | ], 23 | 'sonatype' : [ 24 | 'generatePomFileForInternalPublication', 25 | 'publishMavenJavaPublicationToOssrhRepository', 26 | 'closeAndReleaseRepository' 27 | ], 28 | 'deploy' : [ 29 | 'uploadArchives', 30 | 'closeAndReleaseRepository' 31 | ], 32 | 'classpath' : [ 33 | 'cleanEclipseClasspath', 34 | 'eclipseClasspath', 35 | 'eclipseFactoryPath', 36 | 'cleanIdeaModule', 37 | 'ideaModule' 38 | ], 39 | ] 40 | 41 | // package patterns to exclude from Eclipse 42 | excludeFromEclipse = [] 43 | } 44 | 45 | 46 | // ------------------------------------------- 47 | // REPOSITORIES / PUBLISHING 48 | // ------------------------------------------- 49 | 50 | repositories { 51 | // prefer locally built artifacts 52 | mavenLocal() 53 | 54 | // use external repos as fallback 55 | mavenCentral() 56 | } 57 | 58 | task sourcesJar(type: Jar) { 59 | archiveClassifier.set('sources') 60 | from sourceSets.main.allJava 61 | } 62 | 63 | task javadocJar(type: Jar, dependsOn: classes) { 64 | archiveClassifier.set('javadoc') 65 | from javadoc 66 | } 67 | 68 | task testsJar(type:Jar, dependsOn: testClasses) { 69 | archiveClassifier.set('tests') 70 | from sourceSets.test.output 71 | } 72 | 73 | test { 74 | // enable JUnit 5 tests 75 | useJUnitPlatform() 76 | } 77 | 78 | 79 | // ------------------------------------------- 80 | // JAVA COMPILER 81 | // ------------------------------------------- 82 | 83 | tasks.withType(JavaCompile) { task -> 84 | sourceCompatibility = 11 85 | targetCompatibility = 11 86 | 87 | // always UTF-8 88 | options.encoding = 'UTF-8' 89 | 90 | // java 8 option which export names of constructor and method parameter names; no longer 91 | // have to declare parameter names with @JsonCreator 92 | options.compilerArgs << "-parameters" 93 | 94 | options.compilerArgs << '-Xlint:unchecked' 95 | 96 | if (project.plugins.hasPlugin('net.ltgt.errorprone')) { 97 | // Eclipse code formatting removes extraneous parenthesis which errorprone complains about 98 | options.errorprone.disable 'OperatorPrecedence' 99 | 100 | // we don't need to check return value always 101 | options.errorprone.disable 'FutureReturnValueIgnored' 102 | 103 | // generated code can have lots of bogus warnings 104 | options.errorprone.disableWarningsInGeneratedCode = true 105 | 106 | // bogus warning 107 | options.errorprone.disable 'StringSplitter' 108 | 109 | // ignore all generated source folders 110 | options.errorprone.excludedPaths = '.*generated.*' 111 | } 112 | } 113 | 114 | 115 | // ------------------------------------------- 116 | // SHADOW JAR 117 | // ------------------------------------------- 118 | 119 | if(plugins.hasPlugin("com.github.johnrengelman.shadow")) { 120 | jar { 121 | archiveClassifier.set('original') 122 | } 123 | 124 | shadowJar { 125 | archiveClassifier.set('shadow') 126 | } 127 | 128 | sourceSets { 129 | // shadow configuration is added by Shadow plugin, but it's only configured for the main sourceset 130 | test.compileClasspath += configurations.shadow 131 | test.runtimeClasspath += configurations.shadow 132 | } 133 | } 134 | 135 | 136 | publishing { 137 | publications { 138 | mavenJava(MavenPublication) { 139 | from components.java 140 | } 141 | } 142 | } 143 | 144 | publishing { 145 | if(project.hasProperty('nexusUsername')) { 146 | repositories { 147 | maven { 148 | name = "ossrh" 149 | url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 150 | credentials { 151 | username = project.nexusUsername 152 | password = project.nexusPassword 153 | } 154 | } 155 | } 156 | } 157 | 158 | publications { 159 | mavenJava { 160 | artifact sourcesJar 161 | artifact javadocJar 162 | artifact testsJar 163 | 164 | versionMapping { 165 | usage('java-api') { 166 | fromResolutionOf('runtimeClasspath') 167 | } 168 | usage('java-runtime') { 169 | fromResolutionResult() 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | signing { 177 | sign publishing.publications.mavenJava 178 | } 179 | 180 | nexusStaging { 181 | repositoryDescription project.name 182 | } 183 | 184 | 185 | // ------------------------------------------- 186 | // ECLIPSE 187 | // ------------------------------------------- 188 | 189 | eclipse { 190 | classpath { 191 | // override default 'bin' 192 | defaultOutputDir = project.file('bin/classes') 193 | 194 | // we want source files 195 | downloadSources = true 196 | downloadJavadoc = false 197 | 198 | // customize generated .classpath file 199 | file { 200 | def project_refs = [] 201 | def remove_entries = [] 202 | 203 | // closure executed after .classpath content is loaded from existing file 204 | // and after gradle build information is merged 205 | whenMerged { classpath -> 206 | 207 | // build list of dependencies that we want to replace with Eclipse project refs 208 | println 'Finding local projects' 209 | def use_eclipse_project_refs = [] 210 | new File(project.projectDir, "..").eachDir { 211 | if(new File("${it}/build.gradle").exists()) { 212 | use_eclipse_project_refs.add it.name 213 | } 214 | } 215 | 216 | println 'Generating Eclipse .classpath file' 217 | def kindOrder = [ 'src':1, 'con':2, 'lib':3, 'output':0 ]; 218 | classpath.entries.sort(true, { a,b -> 219 | def order = kindOrder[a.kind] <=> kindOrder[b.kind] 220 | order != 0 ? order : a.path <=> b.path 221 | } as Comparator).each { entry -> 222 | if(entry.kind.equals('lib')) { 223 | use_eclipse_project_refs.each { name -> 224 | def regex = '/(' + ( name.endsWith('-') ? 225 | java.util.regex.Pattern.quote(name.substring(0,name.length()-1)) + '(?:-[A-Za-z]+)*' 226 | : java.util.regex.Pattern.quote(name) ) + ')-([\\w\\.]+?)(-[A-Za-z]+)?\\.jar$' 227 | def pattern = java.util.regex.Pattern.compile(regex) 228 | def matcher = pattern.matcher(entry.path) 229 | if(matcher.find()) { 230 | def match = matcher.group(1) 231 | println match + ' (' + matcher.group(2) + ') matched ' + entry.path 232 | remove_entries += [entry] 233 | project_refs += [match] 234 | } 235 | } 236 | entry.exported = true 237 | } else if(entry.kind.equals('src')) { 238 | project.ext.excludeFromEclipse.each { path -> 239 | if(entry.path.equals(path)) { 240 | remove_entries += [entry] 241 | } 242 | } 243 | } 244 | } 245 | classpath.entries.removeAll(remove_entries) 246 | } 247 | 248 | // final adjustments to .classpath file before it is saved 249 | withXml { xml -> 250 | def node = xml.asNode() 251 | 252 | project_refs.unique(false).each { name -> 253 | println "Creating Eclipse project dependency: " + name 254 | node.appendNode('classpathentry', [ combineaccessrules: false, exported: true, kind: 'src', path: '/' + name ]) 255 | } 256 | 257 | def apt = ['.apt_generated_test': 'bin/test', 258 | '.apt_generated': 'bin/main'] 259 | 260 | apt.each { path, output -> 261 | def atts = node.appendNode('classpathentry', [kind:'src', output: output, path: path]).appendNode('attributes') 262 | atts.appendNode('attribute', [name: 'ignore_optional_problems', value: true]) 263 | atts.appendNode('attribute', [name: 'test', value: output.contains('test')]) 264 | atts.appendNode('attribute', [name: 'optional', value: true]) 265 | } 266 | } 267 | } 268 | } 269 | } 270 | 271 | 272 | // ------------------------------------------- 273 | // README 274 | // ------------------------------------------- 275 | 276 | task readme { 277 | ant.replaceregexp(match:'\\([0-9\\.]+)\\<\\/version\\>', replace:"${version}", flags:'g', byline:true) { 278 | fileset(dir: '.', includes: 'README.md') 279 | } 280 | ant.replaceregexp(match:'com\\.arakelian\\:' + project.name + ':([0-9\\.]+)', replace:"com.arakelian:${project.name}:${version}", flags:'g', byline:true) { 281 | fileset(dir: '.', includes: 'README.md') 282 | } 283 | } 284 | 285 | 286 | // ------------------------------------------- 287 | // SHORTCUT TASKS 288 | // ------------------------------------------- 289 | 290 | 291 | // This code allows us to define aliases, such as "all", so that when we do "gradle all", 292 | // we can substitute in a series of other gradle tasks 293 | // see: https://caffeineinduced.wordpress.com/2015/01/25/run-a-list-of-gradle-tasks-in-specific-order/ 294 | def newTasks = [] 295 | 296 | // gradle respects ordering of tasks specified on command line, so we replace shortcuts 297 | // with equivalent commands as though they were specified by user 298 | gradle.startParameter.taskNames.each { param -> 299 | def macro = project.ext.macros[param] 300 | if( macro ) { 301 | macro.each { task -> 302 | if(project.tasks.names.contains(task)) { 303 | newTasks << task 304 | } 305 | } 306 | } else { 307 | newTasks << param 308 | } 309 | } 310 | 311 | // replace command line arguments 312 | gradle.startParameter.taskNames = newTasks.flatten() 313 | -------------------------------------------------------------------------------- /docker-build.sh: -------------------------------------------------------------------------------- 1 | SCRIPT_PATH=$( cd "$(dirname "$0")" ; pwd -P ) 2 | PROJECT=$(basename "${SCRIPT_PATH}") 3 | JQ_VERSION=1.6 4 | 5 | set -e 6 | 7 | # build linux version 8 | docker build --tag ${PROJECT} ${SCRIPT_PATH}/. 9 | 10 | # get file 11 | mkdir -p src/main/resources/lib/linux-x86_64 12 | docker run --rm -v $(pwd -P)/src/main/resources/lib/linux-x86_64:/mnt ${PROJECT} cp /root/build/jq-${JQ_VERSION}/.libs/libjq.so /mnt 13 | echo "Linux-x86_64 JQ build successfully." 14 | 15 | 16 | # build aarch64 linux version 17 | docker build --platform=linux/arm64 --tag ${PROJECT}_aarch64 -f Dockerfile.aarch64 ${SCRIPT_PATH}/. 18 | 19 | # get file 20 | mkdir -p src/main/resources/lib/linux-aarch64 21 | docker run --rm -v $(pwd -P)/src/main/resources/lib/linux-aarch64:/mnt ${PROJECT}_aarch64 cp /home/libjq.so /mnt 22 | echo "Linux-aarch64 JQ build successfully." 23 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arakelian/java-jq/e2ec113e89430025b48b4fcca748a309731a03e2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'java-jq' 2 | 3 | -------------------------------------------------------------------------------- /src/main/c/jq.h: -------------------------------------------------------------------------------- 1 | #ifndef JQ_H 2 | #define JQ_H 3 | 4 | #include 5 | #include 6 | 7 | enum {JQ_DEBUG_TRACE = 1}; 8 | 9 | typedef struct jq_state jq_state; 10 | typedef void (*jq_msg_cb)(void *, jv); 11 | 12 | jq_state *jq_init(void); 13 | void jq_set_error_cb(jq_state *, jq_msg_cb, void *); 14 | void jq_get_error_cb(jq_state *, jq_msg_cb *, void **); 15 | void jq_set_nomem_handler(jq_state *, void (*)(void *), void *); 16 | jv jq_format_error(jv msg); 17 | void jq_report_error(jq_state *, jv); 18 | int jq_compile(jq_state *, const char*); 19 | int jq_compile_args(jq_state *, const char*, jv); 20 | void jq_dump_disassembly(jq_state *, int); 21 | void jq_start(jq_state *, jv value, int); 22 | jv jq_next(jq_state *); 23 | void jq_teardown(jq_state **); 24 | 25 | typedef jv (*jq_input_cb)(jq_state *, void *); 26 | void jq_set_input_cb(jq_state *, jq_input_cb, void *); 27 | void jq_get_input_cb(jq_state *, jq_input_cb *, void **); 28 | 29 | void jq_set_debug_cb(jq_state *, jq_msg_cb, void *); 30 | void jq_get_debug_cb(jq_state *, jq_msg_cb *, void **); 31 | 32 | void jq_set_attrs(jq_state *, jv); 33 | jv jq_get_attrs(jq_state *); 34 | jv jq_get_jq_origin(jq_state *); 35 | jv jq_get_prog_origin(jq_state *); 36 | jv jq_get_lib_dirs(jq_state *); 37 | void jq_set_attr(jq_state *, jv, jv); 38 | jv jq_get_attr(jq_state *, jv); 39 | 40 | /* 41 | * We use char * instead of jf for filenames here because filenames 42 | * should be in the process' locale's codeset, which may not be UTF-8, 43 | * whereas jv string values must be in UTF-8. This way the caller 44 | * doesn't have to perform any codeset conversions. 45 | */ 46 | typedef struct jq_util_input_state jq_util_input_state; 47 | typedef void (*jq_util_msg_cb)(void *, const char *); 48 | 49 | jq_util_input_state *jq_util_input_init(jq_util_msg_cb, void *); 50 | void jq_util_input_set_parser(jq_util_input_state *, jv_parser *, int); 51 | void jq_util_input_free(jq_util_input_state **); 52 | void jq_util_input_add_input(jq_util_input_state *, const char *); 53 | int jq_util_input_errors(jq_util_input_state *); 54 | jv jq_util_input_next_input(jq_util_input_state *); 55 | jv jq_util_input_next_input_cb(jq_state *, void *); 56 | jv jq_util_input_get_position(jq_state*); 57 | jv jq_util_input_get_current_filename(jq_state*); 58 | jv jq_util_input_get_current_line(jq_state*); 59 | 60 | #endif /* !JQ_H */ 61 | -------------------------------------------------------------------------------- /src/main/c/jv.h: -------------------------------------------------------------------------------- 1 | #ifndef JV_H 2 | #define JV_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef enum { 9 | JV_KIND_INVALID, 10 | JV_KIND_NULL, 11 | JV_KIND_FALSE, 12 | JV_KIND_TRUE, 13 | JV_KIND_NUMBER, 14 | JV_KIND_STRING, 15 | JV_KIND_ARRAY, 16 | JV_KIND_OBJECT 17 | } jv_kind; 18 | 19 | struct jv_refcnt; 20 | 21 | /* All of the fields of this struct are private. 22 | Really. Do not play with them. */ 23 | typedef struct { 24 | unsigned char kind_flags; 25 | unsigned char pad_; 26 | unsigned short offset; /* array offsets */ 27 | int size; 28 | union { 29 | struct jv_refcnt* ptr; 30 | double number; 31 | } u; 32 | } jv; 33 | 34 | /* 35 | * All jv_* functions consume (decref) input and produce (incref) output 36 | * Except jv_copy 37 | */ 38 | 39 | jv_kind jv_get_kind(jv); 40 | const char* jv_kind_name(jv_kind); 41 | static int jv_is_valid(jv x) { return jv_get_kind(x) != JV_KIND_INVALID; } 42 | 43 | jv jv_copy(jv); 44 | void jv_free(jv); 45 | 46 | int jv_get_refcnt(jv); 47 | 48 | int jv_equal(jv, jv); 49 | int jv_identical(jv, jv); 50 | int jv_contains(jv, jv); 51 | 52 | jv jv_invalid(void); 53 | jv jv_invalid_with_msg(jv); 54 | jv jv_invalid_get_msg(jv); 55 | int jv_invalid_has_msg(jv); 56 | 57 | 58 | jv jv_null(void); 59 | jv jv_true(void); 60 | jv jv_false(void); 61 | jv jv_bool(int); 62 | 63 | jv jv_number(double); 64 | double jv_number_value(jv); 65 | int jv_is_integer(jv); 66 | 67 | jv jv_array(void); 68 | jv jv_array_sized(int); 69 | int jv_array_length(jv); 70 | jv jv_array_get(jv, int); 71 | jv jv_array_set(jv, int, jv); 72 | jv jv_array_append(jv, jv); 73 | jv jv_array_concat(jv, jv); 74 | jv jv_array_slice(jv, int, int); 75 | jv jv_array_indexes(jv, jv); 76 | #define jv_array_foreach(a, i, x) \ 77 | for (int jv_len__ = jv_array_length(jv_copy(a)), i=0, jv_j__ = 1; \ 78 | jv_j__; jv_j__ = 0) \ 79 | for (jv x; \ 80 | i < jv_len__ ? \ 81 | (x = jv_array_get(jv_copy(a), i), 1) : 0; \ 82 | i++) 83 | 84 | #define JV_ARRAY_1(e) (jv_array_append(jv_array(),e)) 85 | #define JV_ARRAY_2(e1,e2) (jv_array_append(JV_ARRAY_1(e1),e2)) 86 | #define JV_ARRAY_3(e1,e2,e3) (jv_array_append(JV_ARRAY_2(e1,e2),e3)) 87 | #define JV_ARRAY_4(e1,e2,e3,e4) (jv_array_append(JV_ARRAY_3(e1,e2,e3),e4)) 88 | #define JV_ARRAY_5(e1,e2,e3,e4,e5) (jv_array_append(JV_ARRAY_4(e1,e2,e3,e4),e5)) 89 | #define JV_ARRAY_6(e1,e2,e3,e4,e5,e6) (jv_array_append(JV_ARRAY_5(e1,e2,e3,e4,e5),e6)) 90 | #define JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7) (jv_array_append(JV_ARRAY_6(e1,e2,e3,e4,e5,e6),e7)) 91 | #define JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8) (jv_array_append(JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7),e8)) 92 | #define JV_ARRAY_9(e1,e2,e3,e4,e5,e6,e7,e8,e9) (jv_array_append(JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8),e9)) 93 | #define JV_ARRAY_IDX(_1,_2,_3,_4,_5,_6,_7,_8,_9,NAME,...) NAME 94 | #define JV_ARRAY(...) \ 95 | JV_ARRAY_IDX(__VA_ARGS__, JV_ARRAY_9, JV_ARRAY_8, JV_ARRAY_7, JV_ARRAY_6, JV_ARRAY_5, JV_ARRAY_4, JV_ARRAY_3, JV_ARRAY_2, JV_ARRAY_1)(__VA_ARGS__) 96 | 97 | #ifdef __GNUC__ 98 | #define JV_PRINTF_LIKE(fmt_arg_num, args_num) \ 99 | __attribute__ ((__format__( __printf__, fmt_arg_num, args_num))) 100 | #define JV_VPRINTF_LIKE(fmt_arg_num) \ 101 | __attribute__ ((__format__( __printf__, fmt_arg_num, 0))) 102 | #endif 103 | 104 | 105 | jv jv_string(const char*); 106 | jv jv_string_sized(const char*, int); 107 | jv jv_string_empty(int len); 108 | int jv_string_length_bytes(jv); 109 | int jv_string_length_codepoints(jv); 110 | unsigned long jv_string_hash(jv); 111 | const char* jv_string_value(jv); 112 | jv jv_string_indexes(jv j, jv k); 113 | jv jv_string_slice(jv j, int start, int end); 114 | jv jv_string_concat(jv, jv); 115 | jv jv_string_vfmt(const char*, va_list) JV_VPRINTF_LIKE(1); 116 | jv jv_string_fmt(const char*, ...) JV_PRINTF_LIKE(1, 2); 117 | jv jv_string_append_codepoint(jv a, uint32_t c); 118 | jv jv_string_append_buf(jv a, const char* buf, int len); 119 | jv jv_string_append_str(jv a, const char* str); 120 | jv jv_string_split(jv j, jv sep); 121 | jv jv_string_explode(jv j); 122 | jv jv_string_implode(jv j); 123 | 124 | jv jv_object(void); 125 | jv jv_object_get(jv object, jv key); 126 | jv jv_object_set(jv object, jv key, jv value); 127 | jv jv_object_delete(jv object, jv key); 128 | int jv_object_length(jv object); 129 | jv jv_object_merge(jv, jv); 130 | jv jv_object_merge_recursive(jv, jv); 131 | 132 | int jv_object_iter(jv); 133 | int jv_object_iter_next(jv, int); 134 | int jv_object_iter_valid(jv, int); 135 | jv jv_object_iter_key(jv, int); 136 | jv jv_object_iter_value(jv, int); 137 | #define jv_object_foreach(t, k, v) \ 138 | for (int jv_i__ = jv_object_iter(t), jv_j__ = 1; jv_j__; jv_j__ = 0) \ 139 | for (jv k, v; \ 140 | jv_object_iter_valid((t), jv_i__) ? \ 141 | (k = jv_object_iter_key(t, jv_i__), \ 142 | v = jv_object_iter_value(t, jv_i__), \ 143 | 1) \ 144 | : 0; \ 145 | jv_i__ = jv_object_iter_next(t, jv_i__)) \ 146 | 147 | #define JV_OBJECT_1(k) (jv_object_set(jv_object(),(k),jv_null())) 148 | #define JV_OBJECT_2(k1,v1) (jv_object_set(jv_object(),(k1),(v1))) 149 | #define JV_OBJECT_3(k1,v1,k2) (jv_object_set(JV_OBJECT_2(k1,v1),k2,jv_null())) 150 | #define JV_OBJECT_4(k1,v1,k2,v2) (jv_object_set(JV_OBJECT_2(k1,v1),k2,v2)) 151 | #define JV_OBJECT_5(k1,v1,k2,v2,k3) (jv_object_set(JV_OBJECT_4(k1,v1,k2,v2),k3,jv_null)) 152 | #define JV_OBJECT_6(k1,v1,k2,v2,k3,v3) (jv_object_set(JV_OBJECT_4(k1,v1,k2,v2),k3,v3)) 153 | #define JV_OBJECT_7(k1,v1,k2,v2,k3,v3,k4) (jv_object_set(JV_OBJECT_6(k1,v1,k2,v2,k3,v3),k4,jv_null())) 154 | #define JV_OBJECT_8(k1,v1,k2,v2,k3,v3,k4,v4) (jv_object_set(JV_OBJECT_6(k1,v1,k2,v2,k3,v3),k4,v4)) 155 | #define JV_OBJECT_IDX(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME 156 | #define JV_OBJECT(...) \ 157 | JV_OBJECT_IDX(__VA_ARGS__, JV_OBJECT_8, JV_OBJECT_7, JV_OBJECT_6, JV_OBJECT_5, JV_OBJECT_4, JV_OBJECT_3, JV_OBJECT_2, JV_OBJECT_1)(__VA_ARGS__) 158 | 159 | 160 | 161 | int jv_get_refcnt(jv); 162 | 163 | enum jv_print_flags { 164 | JV_PRINT_PRETTY = 1, 165 | JV_PRINT_ASCII = 2, 166 | JV_PRINT_COLOUR = 4, 167 | JV_PRINT_SORTED = 8, 168 | JV_PRINT_INVALID = 16, 169 | JV_PRINT_REFCOUNT = 32, 170 | JV_PRINT_TAB = 64, 171 | JV_PRINT_ISATTY = 128, 172 | JV_PRINT_SPACE0 = 256, 173 | JV_PRINT_SPACE1 = 512, 174 | JV_PRINT_SPACE2 = 1024, 175 | }; 176 | #define JV_PRINT_INDENT_FLAGS(n) \ 177 | ((n) < 0 || (n) > 7 ? JV_PRINT_TAB | JV_PRINT_PRETTY : (n) == 0 ? 0 : (n) << 8 | JV_PRINT_PRETTY) 178 | void jv_dumpf(jv, FILE *f, int flags); 179 | void jv_dump(jv, int flags); 180 | void jv_show(jv, int flags); 181 | jv jv_dump_string(jv, int flags); 182 | char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize); 183 | 184 | enum { 185 | JV_PARSE_SEQ = 1, 186 | JV_PARSE_STREAMING = 2, 187 | JV_PARSE_STREAM_ERRORS = 4, 188 | }; 189 | 190 | jv jv_parse(const char* string); 191 | jv jv_parse_sized(const char* string, int length); 192 | 193 | typedef void (*jv_nomem_handler_f)(void *); 194 | void jv_nomem_handler(jv_nomem_handler_f, void *); 195 | 196 | jv jv_load_file(const char *, int); 197 | 198 | typedef struct jv_parser jv_parser; 199 | jv_parser* jv_parser_new(int); 200 | void jv_parser_set_buf(jv_parser*, const char*, int, int); 201 | int jv_parser_remaining(jv_parser*); 202 | jv jv_parser_next(jv_parser*); 203 | void jv_parser_free(jv_parser*); 204 | 205 | jv jv_get(jv, jv); 206 | jv jv_set(jv, jv, jv); 207 | jv jv_has(jv, jv); 208 | jv jv_setpath(jv, jv, jv); 209 | jv jv_getpath(jv, jv); 210 | jv jv_delpaths(jv, jv); 211 | jv jv_keys(jv /*object or array*/); 212 | jv jv_keys_unsorted(jv /*object or array*/); 213 | int jv_cmp(jv, jv); 214 | jv jv_group(jv, jv); 215 | jv jv_sort(jv, jv); 216 | 217 | 218 | #endif 219 | 220 | 221 | /* 222 | 223 | true/false/null: 224 | check kind 225 | 226 | number: 227 | introduce/eliminate jv 228 | to integer 229 | 230 | array: 231 | copy 232 | free 233 | slice 234 | index 235 | update 236 | 237 | updateslice? 238 | 239 | 240 | */ 241 | -------------------------------------------------------------------------------- /src/main/java/com/arakelian/jq/JqLibrary.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import static java.util.logging.Level.INFO; 21 | 22 | import java.util.List; 23 | import java.util.logging.Logger; 24 | 25 | import org.immutables.value.Value; 26 | 27 | import com.google.common.base.Charsets; 28 | import com.google.common.base.Preconditions; 29 | import com.google.common.collect.ImmutableList; 30 | import com.sun.jna.Callback; 31 | import com.sun.jna.Function; 32 | import com.sun.jna.Pointer; 33 | import com.sun.jna.Structure; 34 | import com.sun.jna.Structure.ByReference; 35 | import com.sun.jna.Structure.ByValue; 36 | import com.sun.jna.Union; 37 | import com.sun.jna.ptr.PointerByReference; 38 | 39 | @Value.Immutable(singleton = true) 40 | public abstract class JqLibrary { 41 | public interface ErrorCallback extends Callback { 42 | public void callback(final Pointer data, final Jv jv); 43 | } 44 | 45 | public static class Jv extends Structure implements ByValue { 46 | public static class U extends Union { 47 | public JvRefCount ptr; 48 | public double number; 49 | } 50 | 51 | public byte kind_flags; 52 | public byte pad_; 53 | public short offset; 54 | public int size; 55 | public U u; 56 | 57 | @Override 58 | protected List getFieldOrder() { 59 | return ImmutableList.of("kind_flags", "pad_", "offset", "size", "u"); 60 | } 61 | } 62 | 63 | public static class JvRefCount extends Structure implements ByReference { 64 | public int count; 65 | 66 | @Override 67 | protected List getFieldOrder() { 68 | return ImmutableList.of("count"); 69 | } 70 | } 71 | 72 | private static final Logger LOGGER = Logger.getLogger(JqLibrary.class.getName()); 73 | 74 | public static final int JV_KIND_INVALID = 0; 75 | public static final int JV_KIND_NULL = 1; 76 | public static final int JV_KIND_FALSE = 2; 77 | public static final int JV_KIND_TRUE = 3; 78 | public static final int JV_KIND_NUMBER = 4; 79 | public static final int JV_KIND_STRING = 5; 80 | public static final int JV_KIND_ARRAY = 6; 81 | public static final int JV_KIND_OBJECT = 7; 82 | 83 | public static final int JV_PARSE_SEQ = 1; 84 | public static final int JV_PARSE_STREAMING = 2; 85 | public static final int JV_PARSE_STREAM_ERRORS = 4; 86 | 87 | public static final int JV_PRINT_PRETTY = 1; 88 | public static final int JV_PRINT_ASCII = 2; 89 | public static final int JV_PRINT_COLOR = 4; 90 | public static final int JV_PRINT_SORTED = 8; 91 | public static final int JV_PRINT_INVALID = 16; 92 | public static final int JV_PRINT_REFCOUNT = 32; 93 | public static final int JV_PRINT_TAB = 64; 94 | public static final int JV_PRINT_ISATTY = 128; 95 | public static final int JV_PRINT_SPACE0 = 256; 96 | public static final int JV_PRINT_SPACE1 = 512; 97 | public static final int JV_PRINT_SPACE2 = 1024; 98 | 99 | /** No arguments **/ 100 | public static final Object[] NO_ARGS = new Object[0]; 101 | 102 | @Value.Auxiliary 103 | public Function getJqCompile() { 104 | return getLoader().getNativeLibrary().getFunction("jq_compile"); 105 | } 106 | 107 | @Value.Auxiliary 108 | public Function getJqCompileArgs() { 109 | return getLoader().getNativeLibrary().getFunction("jq_compile_args"); 110 | } 111 | 112 | @Value.Auxiliary 113 | public Function getJqInit() { 114 | return getLoader().getNativeLibrary().getFunction("jq_init"); 115 | } 116 | 117 | @Value.Auxiliary 118 | public Function getJqNext() { 119 | return getLoader().getNativeLibrary().getFunction("jq_next"); 120 | } 121 | 122 | @Value.Auxiliary 123 | public Function getJqSetAttr() { 124 | return getLoader().getNativeLibrary().getFunction("jq_set_attr"); 125 | } 126 | 127 | @Value.Auxiliary 128 | public Function getJqSetErrorCb() { 129 | return getLoader().getNativeLibrary().getFunction("jq_set_error_cb"); 130 | } 131 | 132 | @Value.Auxiliary 133 | public Function getJqStart() { 134 | return getLoader().getNativeLibrary().getFunction("jq_start"); 135 | } 136 | 137 | @Value.Auxiliary 138 | public Function getJqTeardown() { 139 | return getLoader().getNativeLibrary().getFunction("jq_teardown"); 140 | } 141 | 142 | @Value.Auxiliary 143 | public Function getJvArray() { 144 | return getLoader().getNativeLibrary().getFunction("jv_array"); 145 | } 146 | 147 | @Value.Auxiliary 148 | public Function getJvArrayAppend() { 149 | return getLoader().getNativeLibrary().getFunction("jv_array_append"); 150 | } 151 | 152 | @Value.Auxiliary 153 | public Function getJvArrayConcat() { 154 | return getLoader().getNativeLibrary().getFunction("jv_array_concat"); 155 | } 156 | 157 | @Value.Auxiliary 158 | public Function getJvCopy() { 159 | return getLoader().getNativeLibrary().getFunction("jv_copy"); 160 | } 161 | 162 | @Value.Auxiliary 163 | public Function getJvDumpString() { 164 | return getLoader().getNativeLibrary().getFunction("jv_dump_string"); 165 | } 166 | 167 | @Value.Auxiliary 168 | public Function getJvFree() { 169 | return getLoader().getNativeLibrary().getFunction("jv_free"); 170 | } 171 | 172 | @Value.Auxiliary 173 | public Function getJvGetKind() { 174 | return getLoader().getNativeLibrary().getFunction("jv_get_kind"); 175 | } 176 | 177 | @Value.Auxiliary 178 | public Function getJvInvalidGetMsg() { 179 | return getLoader().getNativeLibrary().getFunction("jv_invalid_get_msg"); 180 | } 181 | 182 | @Value.Auxiliary 183 | public Function getJvInvalidHasMsg() { 184 | return getLoader().getNativeLibrary().getFunction("jv_invalid_has_msg"); 185 | } 186 | 187 | @Value.Auxiliary 188 | public Function getJvObject() { 189 | return getLoader().getNativeLibrary().getFunction("jv_object"); 190 | } 191 | 192 | @Value.Auxiliary 193 | public Function getJvObjectHas() { 194 | return getLoader().getNativeLibrary().getFunction("jv_object_has"); 195 | } 196 | 197 | @Value.Auxiliary 198 | public Function getJvObjectSet() { 199 | return getLoader().getNativeLibrary().getFunction("jv_object_set"); 200 | } 201 | 202 | @Value.Auxiliary 203 | public Function getJvParse() { 204 | return getLoader().getNativeLibrary().getFunction("jv_parse"); 205 | } 206 | 207 | @Value.Auxiliary 208 | public Function getJvParserFree() { 209 | return getLoader().getNativeLibrary().getFunction("jv_parser_free"); 210 | } 211 | 212 | @Value.Auxiliary 213 | public Function getJvParserNew() { 214 | return getLoader().getNativeLibrary().getFunction("jv_parser_new"); 215 | } 216 | 217 | @Value.Auxiliary 218 | public Function getJvParserNext() { 219 | return getLoader().getNativeLibrary().getFunction("jv_parser_next"); 220 | } 221 | 222 | @Value.Auxiliary 223 | public Function getJvParserSetBuf() { 224 | return getLoader().getNativeLibrary().getFunction("jv_parser_set_buf"); 225 | } 226 | 227 | @Value.Auxiliary 228 | public Function getJvString() { 229 | return getLoader().getNativeLibrary().getFunction("jv_string"); 230 | } 231 | 232 | @Value.Auxiliary 233 | public Function getJvStringValue() { 234 | return getLoader().getNativeLibrary().getFunction("jv_string_value"); 235 | } 236 | 237 | @Value.Lazy 238 | @Value.Auxiliary 239 | public NativeLib getLoader() { 240 | final ImmutableNativeLib jq = ImmutableNativeLib.builder() // 241 | .name("jq") // 242 | .build(); 243 | Preconditions.checkState(jq.getNativeLibrary() != null, "Cannot load JQ library"); 244 | LOGGER.log(INFO, "Loaded {0}", new Object[] { jq.getLocalCopy() }); 245 | return jq; 246 | } 247 | 248 | public boolean jq_compile(final Pointer jq, final String filter) { 249 | return getJqCompile().invokeInt(new Object[] { jq, filter }) != 0; 250 | } 251 | 252 | public boolean jq_compile_args(final Pointer jq, final String filter, final Jv args) { 253 | return getJqCompileArgs().invokeInt(new Object[] { jq, filter, args }) != 0; 254 | } 255 | 256 | public Pointer jq_init() { 257 | return (Pointer) getJqInit().invoke(Pointer.class, NO_ARGS); 258 | } 259 | 260 | public Jv jq_next(final Pointer jq) { 261 | return (Jv) getJqNext().invoke(Jv.class, new Object[] { jq }); 262 | } 263 | 264 | public void jq_set_attr(final Pointer jq, final Jv name, final Jv value) { 265 | getJqSetAttr().invoke(new Object[] { jq, name, value }); 266 | } 267 | 268 | public void jq_set_error_cb(final Pointer jq, final ErrorCallback callback, final Pointer data) { 269 | getJqSetErrorCb().invoke(new Object[] { jq, callback, data }); 270 | } 271 | 272 | public void jq_start(final Pointer jq, final Jv jv) { 273 | getJqStart().invoke(new Object[] { jq, jv, 0 }); 274 | } 275 | 276 | public void jq_teardown(final Pointer jq) { 277 | final PointerByReference ref = new PointerByReference(jq); 278 | getJqTeardown().invoke(new Object[] { ref }); 279 | } 280 | 281 | public Jv jv_array() { 282 | return (Jv) getJvArray().invoke(Jv.class, new Object[] {}); 283 | } 284 | 285 | public Jv jv_array_append(final Jv array, final Jv value) { 286 | return (Jv) getJvArrayAppend().invoke(Jv.class, new Object[] { array, value }); 287 | } 288 | 289 | public Jv jv_array_concat(final Jv array, final Jv anotherArray) { 290 | return (Jv) getJvArrayConcat().invoke(Jv.class, new Object[] { array, anotherArray }); 291 | } 292 | 293 | public Jv jv_copy(final Jv jv) { 294 | return (Jv) getJvCopy().invoke(Jv.class, new Object[] { jv }); 295 | } 296 | 297 | public String jv_dump_string(final Jv next, final int flags) { 298 | final Jv dumped = (Jv) getJvDumpString().invoke(Jv.class, new Object[] { next, flags }); 299 | try { 300 | return jv_string_value(dumped); 301 | } finally { 302 | jv_free(dumped); 303 | } 304 | } 305 | 306 | public void jv_free(final Jv jv) { 307 | getJvFree().invoke(new Object[] { jv }); 308 | } 309 | 310 | public int jv_get_kind(final Jv jv) { 311 | return getJvGetKind().invokeInt(new Object[] { jv }); 312 | } 313 | 314 | public Jv jv_invalid_get_msg(final Jv jv) { 315 | return (Jv) getJvInvalidGetMsg().invoke(Jv.class, new Object[] { jv }); 316 | } 317 | 318 | public boolean jv_invalid_has_msg(final Jv jv) { 319 | return getJvInvalidHasMsg().invokeInt(new Object[] { jv }) != 0; 320 | } 321 | 322 | public final boolean jv_is_valid(final Jv jv) { 323 | final int kind = getJvGetKind().invokeInt(new Object[] { jv }); 324 | return kind != JqLibrary.JV_KIND_INVALID; 325 | } 326 | 327 | public Jv jv_object() { 328 | return (Jv) getJvObject().invoke(Jv.class, new Object[] {}); 329 | } 330 | 331 | public boolean jv_object_has(final Jv object, final Jv key) { 332 | final int has = getJvObjectHas().invokeInt(new Object[] { object, key }); 333 | return has != 0; 334 | } 335 | 336 | public Jv jv_object_set(final Jv object, final Jv key, final Jv value) { 337 | return (Jv) getJvObjectSet().invoke(Jv.class, new Object[] { object, key, value }); 338 | } 339 | 340 | public Jv jv_parse(final String json) { 341 | return (Jv) getJvParse().invoke(Jv.class, new Object[] { json }); 342 | } 343 | 344 | public void jv_parser_free(final Pointer parser) { 345 | getJvParserFree().invoke(new Object[] { parser }); 346 | } 347 | 348 | public Pointer jv_parser_new(final int flags) { 349 | return (Pointer) getJvParserNew().invoke(Pointer.class, new Object[] { Integer.valueOf(flags) }); 350 | } 351 | 352 | public Jv jv_parser_next(final Pointer parser) { 353 | return (Jv) getJvParserNext().invoke(Jv.class, new Object[] { parser }); 354 | } 355 | 356 | public void jv_parser_set_buf( 357 | final Pointer parser, 358 | final Pointer pointer, 359 | final int length, 360 | final boolean finished) { 361 | getJvParserSetBuf().invoke(new Object[] { parser, pointer, length, finished ? 0 : 1 }); 362 | } 363 | 364 | public Jv jv_string(final String value) { 365 | return (Jv) getJvString().invoke(Jv.class, new Object[] { value }); 366 | } 367 | 368 | public String jv_string_value(final Jv jv) { 369 | final Pointer result = (Pointer) getJvStringValue().invoke(Pointer.class, new Object[] { jv }); 370 | final String error = result.getString(0, Charsets.UTF_8.name()); 371 | return error; 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /src/main/java/com/arakelian/jq/JqRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import static java.util.logging.Level.FINE; 21 | 22 | import java.io.File; 23 | import java.io.IOException; 24 | import java.io.UncheckedIOException; 25 | import java.util.List; 26 | import java.util.Map; 27 | import java.util.concurrent.locks.ReentrantLock; 28 | import java.util.logging.Logger; 29 | 30 | import org.immutables.value.Value; 31 | 32 | import com.arakelian.jq.JqLibrary.Jv; 33 | import com.google.common.base.Charsets; 34 | import com.google.common.base.Preconditions; 35 | import com.google.common.collect.ImmutableMap; 36 | import com.sun.jna.Memory; 37 | import com.sun.jna.Pointer; 38 | 39 | @Value.Immutable 40 | public abstract class JqRequest { 41 | public enum Indent { 42 | NONE, // 43 | TAB, // 44 | SPACE, // 45 | TWO_SPACES; 46 | } 47 | 48 | private static final Logger LOGGER = Logger.getLogger(JqRequest.class.getName()); 49 | 50 | /** 51 | * JQ is not thread-safe - https://github.com/stedolan/jq/issues/120 52 | */ 53 | private static final ReentrantLock SYNC = new ReentrantLock(); 54 | 55 | public final JqResponse execute() { 56 | SYNC.lock(); 57 | try { 58 | return jq(); 59 | } finally { 60 | SYNC.unlock(); 61 | } 62 | } 63 | 64 | @Value.Default 65 | public Map getArgJson() { 66 | return ImmutableMap.of(); 67 | } 68 | 69 | @Value.Derived 70 | @Value.Auxiliary 71 | public int getDumpFlags() { 72 | int flags = 0; 73 | 74 | if (isPretty()) { 75 | flags |= JqLibrary.JV_PRINT_PRETTY; 76 | } 77 | switch (getIndent()) { 78 | case TAB: 79 | flags = JqLibrary.JV_PRINT_TAB; 80 | break; 81 | case SPACE: 82 | flags = JqLibrary.JV_PRINT_SPACE1; 83 | break; 84 | case TWO_SPACES: 85 | flags = JqLibrary.JV_PRINT_SPACE2; 86 | break; 87 | case NONE: 88 | default: 89 | break; 90 | } 91 | 92 | if (isSortKeys()) { 93 | flags |= JqLibrary.JV_PRINT_SORTED; 94 | } 95 | return flags; 96 | } 97 | 98 | @Value.Default 99 | public String getFilter() { 100 | return "."; 101 | } 102 | 103 | @Value.Default 104 | public Indent getIndent() { 105 | return Indent.TWO_SPACES; 106 | } 107 | 108 | public abstract String getInput(); 109 | 110 | public abstract JqLibrary getLib(); 111 | 112 | public abstract List getModulePaths(); 113 | 114 | @Value.Default 115 | public String getStreamSeparator() { 116 | return "\n"; 117 | } 118 | 119 | @Value.Default 120 | public boolean isPretty() { 121 | return true; 122 | } 123 | 124 | @Value.Default 125 | public boolean isSortKeys() { 126 | return false; 127 | } 128 | 129 | /** 130 | * Adds any messages produced by jq native code it to the error store, with the provided prefix. 131 | * 132 | * @param value 133 | * value reference 134 | */ 135 | private String getInvalidMessage(final Jv value) { 136 | final Jv copy = getLib().jv_copy(value); 137 | if (getLib().jv_invalid_has_msg(copy)) { 138 | final Jv message = getLib().jv_invalid_get_msg(value); 139 | return getLib().jv_string_value(message); 140 | } else { 141 | getLib().jv_free(value); 142 | return null; 143 | } 144 | } 145 | 146 | private boolean isValid(final ImmutableJqResponse.Builder response, final Jv value) { 147 | if (getLib().jv_is_valid(value)) { 148 | return true; 149 | } 150 | 151 | // success finishes will return "invalid" value without a message 152 | final String message = getInvalidMessage(value); 153 | if (message != null) { 154 | response.addError(message); 155 | } 156 | return false; 157 | } 158 | 159 | private JqResponse jq() { 160 | LOGGER.log(FINE, "Initializing JQ"); 161 | final JqLibrary lib = getLib(); 162 | final Pointer jq = lib.jq_init(); 163 | Preconditions.checkState(jq != null, "jq must be non-null"); 164 | 165 | Jv moduleDirs = lib.jv_array(); 166 | for (final File file : getModulePaths()) { 167 | try { 168 | final String dir = file.getCanonicalPath(); 169 | LOGGER.log(FINE, "Using module path: " + dir); 170 | moduleDirs = lib.jv_array_append(moduleDirs, lib.jv_string(dir)); 171 | } catch (final IOException e) { 172 | throw new UncheckedIOException(e); 173 | } 174 | } 175 | lib.jq_set_attr(jq, lib.jv_string("JQ_LIBRARY_PATH"), moduleDirs); 176 | 177 | try { 178 | final JqResponse response = parse(jq); 179 | LOGGER.log(FINE, "Response ready"); 180 | return response; 181 | } finally { 182 | LOGGER.log(FINE, "Releasing JQ"); 183 | lib.jq_teardown(jq); 184 | LOGGER.log(FINE, "JQ released successfully"); 185 | } 186 | } 187 | 188 | private JqResponse parse(final Pointer jq) { 189 | final ImmutableJqResponse.Builder response = ImmutableJqResponse.builder(); 190 | 191 | LOGGER.log(FINE, "Configuring callback"); 192 | final JqLibrary lib = getLib(); 193 | lib.jq_set_error_cb(jq, (data, jv) -> { 194 | LOGGER.log(FINE, "Error callback"); 195 | final int kind = lib.jv_get_kind(jv); 196 | if (kind == JqLibrary.JV_KIND_STRING) { 197 | final String error = lib.jv_string_value(jv).replaceAll("\\s++$", ""); 198 | response.addError(error); 199 | } 200 | }, new Pointer(0)); 201 | 202 | // for JQ 1.5, arguments is an array; this changes with JQ 1.6+ 203 | Jv args = lib.jv_object(); 204 | 205 | final Map argJson = getArgJson(); 206 | for (final String varname : argJson.keySet()) { 207 | final String text = argJson.get(varname); 208 | 209 | final Jv json = lib.jv_parse(text); 210 | if (!lib.jv_is_valid(json)) { 211 | response.addError("Invalid JSON text passed to --argjson (name: " + varname + ")"); 212 | return response.build(); 213 | } 214 | 215 | args = lib.jv_object_set(args, lib.jv_string(varname), json); 216 | } 217 | 218 | try { 219 | // compile JQ program 220 | LOGGER.log(FINE, "Compiling filter"); 221 | final String filter = getFilter(); 222 | if (!lib.jq_compile_args(jq, filter, lib.jv_copy(args))) { 223 | // compile errors are captured by callback 224 | LOGGER.log(FINE, "Compilation failed"); 225 | return response.build(); 226 | } 227 | 228 | // create JQ parser 229 | LOGGER.log(FINE, "Creating parse"); 230 | final int parserFlags = 0; 231 | final Pointer parser = lib.jv_parser_new(parserFlags); 232 | try { 233 | parse(jq, parser, getInput(), response); 234 | return response.build(); 235 | } finally { 236 | LOGGER.log(FINE, "Releasing parser"); 237 | lib.jv_parser_free(parser); 238 | } 239 | } finally { 240 | LOGGER.log(FINE, "Releasing callback"); 241 | lib.jq_set_error_cb(jq, null, null); 242 | } 243 | } 244 | 245 | /** 246 | * Add the contents of a native memory array as text to the next chunk of input of the jq 247 | * program. 248 | * 249 | * @param jq 250 | * JQ instance 251 | * @param parser 252 | * JQ parser 253 | * @param text 254 | * input JSON 255 | * @param response 256 | * response that we are building 257 | */ 258 | private void parse( 259 | final Pointer jq, 260 | final Pointer parser, 261 | final String text, 262 | final ImmutableJqResponse.Builder response) { 263 | final byte[] input = text.getBytes(Charsets.UTF_8); 264 | final Memory memory = new Memory(input.length); 265 | memory.write(0, input, 0, input.length); 266 | 267 | // give text to JQ parser 268 | LOGGER.log(FINE, "Sending text to parser"); 269 | getLib().jv_parser_set_buf(parser, memory, input.length, true); 270 | 271 | final int flags = getDumpFlags(); 272 | final StringBuilder buf = new StringBuilder(); 273 | for (;;) { 274 | // iterate until JQ consumes all inputs 275 | LOGGER.log(FINE, "Parsing text"); 276 | final Jv parsed = getLib().jv_parser_next(parser); 277 | if (!isValid(response, parsed)) { 278 | break; 279 | } 280 | 281 | // iterate until we consume all JQ streams 282 | // see: https://stedolan.github.io/jq/tutorial/ 283 | LOGGER.log(FINE, "Consuming JQ response"); 284 | getLib().jq_start(jq, parsed); 285 | for (;;) { 286 | final Jv next = getLib().jq_next(jq); 287 | if (!isValid(response, next)) { 288 | break; 289 | } 290 | 291 | LOGGER.log(FINE, "Dumping response"); 292 | final String out = getLib().jv_dump_string(next, flags); 293 | if (buf.length() != 0) { 294 | buf.append(getStreamSeparator()); 295 | } 296 | buf.append(out); 297 | } 298 | } 299 | 300 | // tell parser we are finished 301 | LOGGER.log(FINE, "Finishing with parser"); 302 | 303 | // finalize output 304 | final String output = buf.toString(); 305 | response.output(output); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /src/main/java/com/arakelian/jq/JqResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import java.util.List; 21 | 22 | import org.immutables.value.Value; 23 | 24 | import com.google.common.collect.ImmutableList; 25 | 26 | @Value.Immutable 27 | public interface JqResponse { 28 | @Value.Default 29 | public default List getErrors() { 30 | return ImmutableList.of(); 31 | } 32 | 33 | @Value.Default 34 | public default String getOutput() { 35 | return ""; 36 | } 37 | 38 | @Value.Derived 39 | @Value.Auxiliary 40 | public default boolean hasErrors() { 41 | return getErrors().size() != 0; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/arakelian/jq/NativeLib.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import static java.util.logging.Level.INFO; 21 | 22 | import java.io.File; 23 | import java.io.FileOutputStream; 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | import java.io.OutputStream; 27 | import java.io.UncheckedIOException; 28 | import java.util.List; 29 | import java.util.Locale; 30 | import java.util.logging.Logger; 31 | 32 | import org.immutables.value.Value; 33 | 34 | import com.google.common.base.Preconditions; 35 | import com.google.common.collect.ImmutableList; 36 | import com.sun.jna.NativeLibrary; 37 | import com.sun.jna.Platform; 38 | 39 | @Value.Immutable(copy = false) 40 | public abstract class NativeLib { 41 | private static final Logger LOGGER = Logger.getLogger(NativeLib.class.getName()); 42 | 43 | @Value.Derived 44 | @Value.Auxiliary 45 | public String getArchitecture() { 46 | return getOsName() + ":" + getOsArch(); 47 | } 48 | 49 | @Value.Default 50 | @Value.Auxiliary 51 | public List getDependencies() { 52 | return ImmutableList.of(); 53 | } 54 | 55 | @Value.Derived 56 | public List getFilenames() { 57 | final ImmutableList.Builder builder = ImmutableList.builder(); 58 | 59 | final String name = getName(); 60 | if (Platform.isWindows()) { 61 | builder.add(name + ".dll"); 62 | } else if (Platform.isLinux()) { 63 | builder.add("lib" + name + ".so"); 64 | } else if (Platform.isMac()) { 65 | builder.add("lib" + name + ".dylib"); 66 | } else { 67 | throw new IllegalStateException("Unsupported architecture: " + getArchitecture()); 68 | } 69 | 70 | for (final String dependency : getDependencies()) { 71 | builder.add(dependency); 72 | } 73 | 74 | return builder.build(); 75 | } 76 | 77 | @Value.Lazy 78 | @Value.Auxiliary 79 | public File getLocalCopy() throws UncheckedIOException { 80 | final File tmpdir = getTemporaryFolder(); 81 | 82 | for (final String filename : getFilenames()) { 83 | try { 84 | final File local = new File(tmpdir, filename); 85 | // local.deleteOnExit(); 86 | 87 | final String resource = "lib/" + getPath() + filename; 88 | LOGGER.log(INFO, "Copying resource {0} to: {1}", new Object[] { resource, local }); 89 | try (InputStream in = this.getClass().getClassLoader().getResourceAsStream(resource); 90 | OutputStream out = new FileOutputStream(local)) { 91 | Preconditions.checkState(in != null, "Cannot find resource %s", resource); 92 | final byte buf[] = new byte[1024 * 1024]; 93 | int n; 94 | while (-1 != (n = in.read(buf))) { 95 | out.write(buf, 0, n); 96 | } 97 | } 98 | } catch (final IOException e) { 99 | throw new UncheckedIOException( 100 | "Unable to copy library " + filename + " to temporary folder " + tmpdir, e); 101 | } 102 | } 103 | 104 | return tmpdir; 105 | } 106 | 107 | public abstract String getName(); 108 | 109 | @Value.Lazy 110 | @Value.Auxiliary 111 | public NativeLibrary getNativeLibrary() throws UncheckedIOException { 112 | final String name = getName(); 113 | 114 | final File libPath = getLocalCopy(); 115 | LOGGER.log(INFO, "{0} library path: {1}", new Object[] { name, libPath }); 116 | NativeLibrary.addSearchPath(name, libPath.getAbsolutePath()); 117 | 118 | final NativeLibrary instance = NativeLibrary.getInstance(name); 119 | LOGGER.log(INFO, "{0} loaded from path: {1} ", new Object[] { name, libPath }); 120 | return instance; 121 | } 122 | 123 | @Value.Derived 124 | public String getOsArch() { 125 | final String arch = System.getProperty("os.arch"); 126 | return arch != null ? arch.toLowerCase(Locale.ROOT) : ""; 127 | } 128 | 129 | @Value.Derived 130 | public String getOsName() { 131 | final String name = System.getProperty("os.name"); 132 | return name != null ? name.toLowerCase(Locale.ROOT) : ""; 133 | } 134 | 135 | @Value.Derived 136 | public String getPath() { 137 | final String osArch = getOsArch(); 138 | if (Platform.isWindows()) { 139 | if ("x86".equalsIgnoreCase(osArch)) { 140 | return "win-x86/"; 141 | } 142 | } else if (Platform.isLinux()) { 143 | if ("amd64".equalsIgnoreCase(osArch)) { 144 | return "linux-x86_64/"; 145 | } else if ("ia64".equalsIgnoreCase(osArch)) { 146 | return "linux-ia64/"; 147 | } else if ("i386".equalsIgnoreCase(osArch)) { 148 | return "linux-x86/"; 149 | } else if ("aarch64".equalsIgnoreCase(osArch)) { 150 | return "linux-aarch64/"; 151 | } 152 | } else if (Platform.isMac()) { 153 | if ("aarch64".equalsIgnoreCase(osArch)) { 154 | return "darwin-aarch64/"; 155 | } else if ("x86_64".equalsIgnoreCase(osArch)) { 156 | return "darwin-x86_64/"; 157 | } 158 | } 159 | throw new IllegalStateException("Unsupported architecture: " + getArchitecture()); 160 | } 161 | 162 | @Value.Default 163 | @Value.Auxiliary 164 | public File getTemporaryFolder() throws UncheckedIOException { 165 | // must create a temp directory, required for NativeLibrary.addSearchPath 166 | File baseDir = new File(System.getProperty("java.io.tmpdir")); 167 | String baseName = System.currentTimeMillis() + "-"; 168 | 169 | for (int counter = 0; counter < 10000; counter++) { 170 | File tmpDir = new File(baseDir, baseName + counter); 171 | if (tmpDir.mkdir()) { 172 | return tmpDir; 173 | } 174 | } 175 | throw new IllegalStateException("Failed to create temp directory"); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/com/arakelian/jq/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | @Value.Style(get = { "is*", "get*" }, depluralize = true) 19 | package com.arakelian.jq; 20 | 21 | import org.immutables.value.Value; 22 | -------------------------------------------------------------------------------- /src/main/resources/lib/darwin-aarch64/libjq.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arakelian/java-jq/e2ec113e89430025b48b4fcca748a309731a03e2/src/main/resources/lib/darwin-aarch64/libjq.dylib -------------------------------------------------------------------------------- /src/main/resources/lib/darwin-x86_64/libjq.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arakelian/java-jq/e2ec113e89430025b48b4fcca748a309731a03e2/src/main/resources/lib/darwin-x86_64/libjq.dylib -------------------------------------------------------------------------------- /src/main/resources/lib/linux-aarch64/libjq.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arakelian/java-jq/e2ec113e89430025b48b4fcca748a309731a03e2/src/main/resources/lib/linux-aarch64/libjq.so -------------------------------------------------------------------------------- /src/main/resources/lib/linux-x86_64/libjq.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arakelian/java-jq/e2ec113e89430025b48b4fcca748a309731a03e2/src/main/resources/lib/linux-x86_64/libjq.so -------------------------------------------------------------------------------- /src/test/java/com/arakelian/jq/AbstractJqTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals; 21 | import static org.junit.jupiter.api.Assertions.assertEquals; 22 | import static org.junit.jupiter.api.Assertions.assertNotNull; 23 | import static org.junit.jupiter.api.Assertions.assertTrue; 24 | 25 | import java.io.BufferedReader; 26 | import java.io.File; 27 | import java.io.IOException; 28 | import java.io.StringReader; 29 | import java.net.URL; 30 | import java.util.Collection; 31 | import java.util.List; 32 | import java.util.logging.LogManager; 33 | 34 | import org.junit.jupiter.api.BeforeAll; 35 | import org.opentest4j.AssertionFailedError; 36 | 37 | import com.arakelian.jq.JqRequest.Indent; 38 | import com.google.common.base.Charsets; 39 | import com.google.common.base.Preconditions; 40 | import com.google.common.collect.Lists; 41 | import com.google.common.io.Resources; 42 | 43 | import net.javacrumbs.jsonunit.JsonAssert; 44 | 45 | public abstract class AbstractJqTest { 46 | public static class JqTestParser { 47 | private final String resource; 48 | 49 | public JqTestParser(final String resource) { 50 | this.resource = resource; 51 | } 52 | 53 | public Collection data() throws IOException { 54 | final URL url = JqTest.class.getResource(resource); 55 | assertTrue(url != null, "Resource does not exist: " + resource); 56 | final String content = Resources.toString(url, Charsets.UTF_8); 57 | 58 | final List data = Lists.newArrayList(); 59 | 60 | try (BufferedReader r = new BufferedReader(new StringReader(content))) { 61 | int lineNo = 0; 62 | for (;;) { 63 | lineNo++; 64 | String line = r.readLine(); 65 | if (line == null) { 66 | break; 67 | } 68 | if (skip(line)) { 69 | continue; 70 | } 71 | 72 | final int lineNumber = lineNo; 73 | final Type type; 74 | final String program; 75 | final String input; 76 | 77 | if (mustFail(line)) { 78 | type = Type.MUST_FAIL; 79 | lineNo++; 80 | program = readLine(r); 81 | input = ""; 82 | } else if (mustFailIgnoreMessage(line)) { 83 | type = Type.MUST_FAIL_IGNORE_MESSAGE; 84 | lineNo++; 85 | program = readLine(r); 86 | input = ""; 87 | } else { 88 | type = Type.MUST_PASS; 89 | lineNo++; 90 | program = line; 91 | input = readLine(r); 92 | } 93 | 94 | final StringBuilder expected = new StringBuilder(); 95 | for (;;) { 96 | lineNo++; 97 | line = r.readLine(); 98 | if (skip(line)) { 99 | break; 100 | } 101 | if (expected.length() != 0) { 102 | expected.append('\n'); 103 | } 104 | expected.append(line); 105 | } 106 | final String testName = "Line " + lineNumber + ": " + program; 107 | data.add(new Object[] { testName, type, program, input, expected.toString() }); 108 | } 109 | } 110 | return data; 111 | } 112 | 113 | private boolean mustFail(final String line) { 114 | return "%%FAIL".equalsIgnoreCase(line); 115 | } 116 | 117 | private boolean mustFailIgnoreMessage(final String line) { 118 | return "%%FAIL IGNORE MSG".equalsIgnoreCase(line); 119 | } 120 | 121 | private String readLine(final BufferedReader r) throws IOException { 122 | final String line = r.readLine(); 123 | Preconditions.checkState(line != null, "unexpected end of file"); 124 | return line; 125 | } 126 | 127 | private boolean skip(final String line) { 128 | if (line == null) { 129 | return true; 130 | } 131 | final int length = line.length(); 132 | if (length == 0) { 133 | return true; 134 | } 135 | for (int i = 0; i < length; i++) { 136 | final char ch = line.charAt(i); 137 | if (ch == ' ' || ch == '\t') { 138 | continue; 139 | } 140 | if (ch == '#') { 141 | return true; 142 | } 143 | break; 144 | } 145 | return false; 146 | } 147 | } 148 | 149 | public enum Type { 150 | MUST_PASS, MUST_FAIL, MUST_FAIL_IGNORE_MESSAGE; 151 | } 152 | 153 | private static final JqLibrary library = ImmutableJqLibrary.of(); 154 | 155 | @BeforeAll 156 | public static void logging() throws SecurityException, IOException { 157 | final LogManager manager = LogManager.getLogManager(); 158 | manager.readConfiguration(AbstractJqTest.class.getResourceAsStream("/logging.properties")); 159 | JsonAssert.setTolerance(0.01); 160 | } 161 | 162 | protected String testName; 163 | protected Type type; 164 | protected String input; 165 | protected String[] expected; 166 | protected String program; 167 | 168 | private void assertFail(final JqResponse response) { 169 | final List errors = response.getErrors(); 170 | assertTrue(errors.size() > 0, "Expected at least one error"); 171 | 172 | String error = errors.get(0); 173 | final int pos = error.indexOf('\n'); 174 | if (pos != -1) { 175 | error = error.substring(0, pos); 176 | } 177 | assertEquals(1, expected.length); 178 | assertEquals(error, expected[0]); 179 | } 180 | 181 | private void assertPass(final JqResponse response) { 182 | assertTrue(!response.hasErrors(), response.getErrors().toString()); 183 | final String[] actual = response.getOutput().split("\n"); 184 | 185 | assertEquals(expected.length, actual.length); 186 | for (int i = 0; i < expected.length; i++) { 187 | try { 188 | assertJsonEquals(expected[i], actual[i]); 189 | } catch (final AssertionFailedError e) { 190 | assertEquals(expected[i], actual[i]); 191 | } 192 | } 193 | } 194 | 195 | protected void test( 196 | final String testName, 197 | final Type type, 198 | final String program, 199 | final String input, 200 | final String expected) { 201 | this.testName = testName; 202 | this.type = type; 203 | this.input = input; 204 | this.expected = expected.split("\\n"); 205 | this.program = program; 206 | 207 | final URL a_jq = getClass().getClassLoader().getResource("modules/a.jq"); 208 | assertNotNull(a_jq != null, "Cannot find path of a.jq"); 209 | final File modulePath = new File(a_jq.getFile()).getParentFile(); 210 | assertTrue(modulePath.exists()); 211 | assertTrue(modulePath.isDirectory()); 212 | 213 | final JqRequest request = ImmutableJqRequest.builder() // 214 | .lib(library) // 215 | .input(input) // 216 | .filter(program) // 217 | .pretty(false) // 218 | .indent(Indent.SPACE) // 219 | .addModulePath(modulePath) // 220 | .build(); 221 | 222 | final JqResponse response = request.execute(); 223 | switch (type) { 224 | case MUST_FAIL: 225 | // fall through 226 | assertFail(response); 227 | break; 228 | case MUST_FAIL_IGNORE_MESSAGE: 229 | assertTrue(response.hasErrors(), "Expected failure"); 230 | break; 231 | case MUST_PASS: 232 | assertPass(response); 233 | break; 234 | default: 235 | throw new IllegalStateException("Unknown test type"); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/test/java/com/arakelian/jq/Base64Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import java.io.IOException; 21 | import java.util.Collection; 22 | 23 | import org.junit.jupiter.params.ParameterizedTest; 24 | import org.junit.jupiter.params.provider.MethodSource; 25 | 26 | public class Base64Test extends AbstractJqTest { 27 | public static Collection base64Test() throws IOException { 28 | return new JqTestParser("/base64.test").data(); 29 | } 30 | 31 | @ParameterizedTest(name = "{0}") 32 | @MethodSource 33 | public void base64Test(String testName, Type type, String program, String input, String expected) { 34 | test(testName, type, program, input, expected); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/arakelian/jq/JqTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import java.io.IOException; 21 | import java.util.Collection; 22 | 23 | import org.junit.jupiter.params.ParameterizedTest; 24 | import org.junit.jupiter.params.provider.MethodSource; 25 | 26 | public class JqTest extends AbstractJqTest { 27 | public static Collection jqTest() throws IOException { 28 | return new JqTestParser("/jq.test").data(); 29 | } 30 | 31 | @ParameterizedTest(name = "{0}") 32 | @MethodSource 33 | public void jqTest(String testName, Type type, String program, String input, String expected) { 34 | test(testName, type, program, input, expected); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/arakelian/jq/OnigurumaTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import java.io.IOException; 21 | import java.util.Collection; 22 | 23 | import org.junit.jupiter.params.ParameterizedTest; 24 | import org.junit.jupiter.params.provider.MethodSource; 25 | 26 | public class OnigurumaTest extends AbstractJqTest { 27 | public static Collection onigurumaTest() throws IOException { 28 | return new JqTestParser("/onig.test").data(); 29 | } 30 | 31 | @ParameterizedTest(name = "{0}") 32 | @MethodSource 33 | public void onigurumaTest(String testName, Type type, String program, String input, String expected) { 34 | test(testName, type, program, input, expected); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/arakelian/jq/OptionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.arakelian.jq; 19 | 20 | import java.io.IOException; 21 | import java.util.Collection; 22 | 23 | import org.junit.jupiter.params.ParameterizedTest; 24 | import org.junit.jupiter.params.provider.MethodSource; 25 | 26 | public class OptionalTest extends AbstractJqTest { 27 | public static Collection optionalTest() throws IOException { 28 | return new JqTestParser("/optional.test").data(); 29 | } 30 | 31 | @ParameterizedTest(name = "{0}") 32 | @MethodSource 33 | public void optionalTest(String testName, Type type, String program, String input, String expected) { 34 | test(testName, type, program, input, expected); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/resources/base64.test: -------------------------------------------------------------------------------- 1 | # Tests are groups of three lines: program, input, expected output 2 | # Blank lines and lines starting with # are ignored 3 | 4 | @base64 5 | "<>&'\"\t" 6 | "PD4mJyIJ" 7 | 8 | # decoding encoded output results in same text 9 | (@base64|@base64d) 10 | "<>&'\"\t" 11 | "<>&'\"\t" 12 | 13 | # regression test for #436 14 | @base64 15 | "foóbar\n" 16 | "Zm/Ds2Jhcgo=" 17 | 18 | @base64d 19 | "Zm/Ds2Jhcgo=" 20 | "foóbar\n" 21 | 22 | # optional trailing equals padding (With padding, this is cWl4YmF6Cg==) 23 | @base64d 24 | "cWl4YmF6Cg" 25 | "qixbaz\n" 26 | 27 | # invalid base64 characters (whitespace) 28 | . | try @base64d catch . 29 | "Not base64 data" 30 | "string (\"Not base64...) is not valid base64 data" 31 | 32 | # invalid base64 (too many bytes, QUJD = "ABCD" 33 | . | try @base64d catch . 34 | "QUJDa" 35 | "string (\"QUJDa\") trailing base64 byte found" 36 | -------------------------------------------------------------------------------- /src/test/resources/jq.test: -------------------------------------------------------------------------------- 1 | # Tests are groups of three lines: program, input, expected output 2 | # Blank lines and lines starting with # are ignored 3 | 4 | # 5 | # Simple value tests to check parser. Input is irrelevant 6 | # 7 | 8 | true 9 | null 10 | true 11 | 12 | false 13 | null 14 | false 15 | 16 | null 17 | 42 18 | null 19 | 20 | 1 21 | null 22 | 1 23 | 24 | 25 | -1 26 | null 27 | -1 28 | 29 | # FIXME: much more number testing needed 30 | 31 | {} 32 | null 33 | {} 34 | 35 | [] 36 | null 37 | [] 38 | 39 | {x: -1} 40 | null 41 | {"x": -1} 42 | 43 | # The input line starts with a 0xFEFF (byte order mark) codepoint 44 | # No, there is no reason to have a byte order mark in UTF8 text. 45 | # But apparently people do, so jq shouldn't break on it. 46 | . 47 | "byte order mark" 48 | "byte order mark" 49 | 50 | # We test escapes by matching them against Unicode codepoints 51 | # FIXME: more tests needed for weird unicode stuff (e.g. utf16 pairs) 52 | "Aa\r\n\t\b\f\u03bc" 53 | null 54 | "Aa\u000d\u000a\u0009\u0008\u000c\u03bc" 55 | 56 | . 57 | "Aa\r\n\t\b\f\u03bc" 58 | "Aa\u000d\u000a\u0009\u0008\u000c\u03bc" 59 | 60 | "inter\("pol" + "ation")" 61 | null 62 | "interpolation" 63 | 64 | @text,@json,([1,.] | (@csv, @tsv)),@html,@uri,@sh,@base64,(@base64 | @base64d) 65 | "<>&'\"\t" 66 | "<>&'\"\t" 67 | "\"<>&'\\\"\\t\"" 68 | "1,\"<>&'\"\"\t\"" 69 | "1\t<>&'\"\\t" 70 | "<>&'"\t" 71 | "%3C%3E%26'%22%09" 72 | "'<>&'\\''\"\t'" 73 | "PD4mJyIJ" 74 | "<>&'\"\t" 75 | 76 | # regression test for #436 77 | @base64 78 | "foóbar\n" 79 | "Zm/Ds2Jhcgo=" 80 | 81 | @base64d 82 | "Zm/Ds2Jhcgo=" 83 | "foóbar\n" 84 | 85 | @uri 86 | "\u03bc" 87 | "%CE%BC" 88 | 89 | @html "\(.)" 90 | "" 91 | "<script>hax</script>" 92 | 93 | [.[]|tojson|fromjson] 94 | ["foo", 1, ["a", 1, "b", 2, {"foo":"bar"}]] 95 | ["foo",1,["a",1,"b",2,{"foo":"bar"}]] 96 | 97 | # 98 | # Dictionary construction syntax 99 | # 100 | 101 | {a: 1} 102 | null 103 | {"a":1} 104 | 105 | {a,b,(.d):.a,e:.b} 106 | {"a":1, "b":2, "c":3, "d":"c"} 107 | {"a":1, "b":2, "c":1, "e":2} 108 | 109 | {"a",b,"a$\(1+1)"} 110 | {"a":1, "b":2, "c":3, "a$2":4} 111 | {"a":1, "b":2, "a$2":4} 112 | 113 | %%FAIL 114 | {(0):1} 115 | jq: error: Cannot use number (0) as object key at , line 1: 116 | 117 | %%FAIL 118 | {non_const:., (0):1} 119 | jq: error: Cannot use number (0) as object key at , line 1: 120 | 121 | # 122 | # Field access, piping 123 | # 124 | 125 | .foo 126 | {"foo": 42, "bar": 43} 127 | 42 128 | 129 | .foo | .bar 130 | {"foo": {"bar": 42}, "bar": "badvalue"} 131 | 42 132 | 133 | .foo.bar 134 | {"foo": {"bar": 42}, "bar": "badvalue"} 135 | 42 136 | 137 | .foo_bar 138 | {"foo_bar": 2} 139 | 2 140 | 141 | .["foo"].bar 142 | {"foo": {"bar": 42}, "bar": "badvalue"} 143 | 42 144 | 145 | ."foo"."bar" 146 | {"foo": {"bar": 20}} 147 | 20 148 | 149 | [.[]|.foo?] 150 | [1,[2],{"foo":3,"bar":4},{},{"foo":5}] 151 | [3,null,5] 152 | 153 | [.[]|.foo?.bar?] 154 | [1,[2],[],{"foo":3},{"foo":{"bar":4}},{}] 155 | [4,null] 156 | 157 | [..] 158 | [1,[[2]],{ "a":[1]}] 159 | [[1,[[2]],{"a":[1]}],1,[[2]],[2],2,{"a":[1]},[1],1] 160 | 161 | [.[]|.[]?] 162 | [1,null,[],[1,[2,[[3]]]],[{}],[{"a":[1,[2]]}]] 163 | [1,[2,[[3]]],{},{"a":[1,[2]]}] 164 | 165 | [.[]|.[1:3]?] 166 | [1,null,true,false,"abcdef",{},{"a":1,"b":2},[],[1,2,3,4,5],[1,2]] 167 | [null,"bc",[],[2,3],[2]] 168 | 169 | # 170 | # Negative array indices 171 | # 172 | 173 | try (.foo[-1] = 0) catch . 174 | null 175 | "Out of bounds negative array index" 176 | 177 | try (.foo[-2] = 0) catch . 178 | null 179 | "Out of bounds negative array index" 180 | 181 | .[-1] = 5 182 | [0,1,2] 183 | [0,1,5] 184 | 185 | .[-2] = 5 186 | [0,1,2] 187 | [0,5,2] 188 | 189 | # 190 | # Multiple outputs, iteration 191 | # 192 | 193 | .[] 194 | [1,2,3] 195 | 1 196 | 2 197 | 3 198 | 199 | 1,1 200 | [] 201 | 1 202 | 1 203 | 204 | 1,. 205 | [] 206 | 1 207 | [] 208 | 209 | [.] 210 | [2] 211 | [[2]] 212 | 213 | [[2]] 214 | [3] 215 | [[2]] 216 | 217 | [{}] 218 | [2] 219 | [{}] 220 | 221 | [.[]] 222 | ["a"] 223 | ["a"] 224 | 225 | [(.,1),((.,.[]),(2,3))] 226 | ["a","b"] 227 | [["a","b"],1,["a","b"],"a","b",2,3] 228 | 229 | [([5,5][]),.,.[]] 230 | [1,2,3] 231 | [5,5,[1,2,3],1,2,3] 232 | 233 | {x: (1,2)},{x:3} | .x 234 | null 235 | 1 236 | 2 237 | 3 238 | 239 | .[-2] 240 | [1,2,3] 241 | 2 242 | 243 | [range(0;10)] 244 | null 245 | [0,1,2,3,4,5,6,7,8,9] 246 | 247 | [range(0,1;3,4)] 248 | null 249 | [0,1,2, 0,1,2,3, 1,2, 1,2,3] 250 | 251 | [range(0;10;3)] 252 | null 253 | [0,3,6,9] 254 | 255 | [range(0;10;-1)] 256 | null 257 | [] 258 | 259 | [range(0;-5;-1)] 260 | null 261 | [0,-1,-2,-3,-4] 262 | 263 | [range(0,1;4,5;1,2)] 264 | null 265 | [0,1,2,3,0,2, 0,1,2,3,4,0,2,4, 1,2,3,1,3, 1,2,3,4,1,3] 266 | 267 | [while(.<100; .*2)] 268 | 1 269 | [1,2,4,8,16,32,64] 270 | 271 | [(label $here | .[] | if .>1 then break $here else . end), "hi!"] 272 | [0,1,2] 273 | [0,1,"hi!"] 274 | 275 | [(label $here | .[] | if .>1 then break $here else . end), "hi!"] 276 | [0,2,1] 277 | [0,"hi!"] 278 | 279 | %%FAIL 280 | . as $foo | break $foo 281 | jq: error: $*label-foo is not defined at , line 1: 282 | 283 | [.[]|[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]] 284 | [1,2,3,4,5] 285 | [1,2,6,24,120] 286 | 287 | [label $out | foreach .[] as $item ([3, null]; if .[0] < 1 then break $out else [.[0] -1, $item] end; .[1])] 288 | [11,22,33,44,55,66,77,88,99] 289 | [11,22,33] 290 | 291 | [foreach range(5) as $item (0; $item)] 292 | null 293 | [0,1,2,3,4] 294 | 295 | [foreach .[] as [$i, $j] (0; . + $i - $j)] 296 | [[2,1], [5,3], [6,4]] 297 | [1,3,5] 298 | 299 | [foreach .[] as {a:$a} (0; . + $a; -.)] 300 | [{"a":1}, {"b":2}, {"a":3, "b":4}] 301 | [-1, -1, -4] 302 | 303 | [limit(3; .[])] 304 | [11,22,33,44,55,66,77,88,99] 305 | [11,22,33] 306 | 307 | [first(range(.)), last(range(.)), nth(0; range(.)), nth(5; range(.)), try nth(-1; range(.)) catch .] 308 | 10 309 | [0,9,0,5,"nth doesn't support negative indices"] 310 | 311 | # Check that first(g) does not extract more than one value from g 312 | first(1,error("foo")) 313 | null 314 | 1 315 | 316 | # 317 | # Check that various builtins evalute all arguments where appropriate, 318 | # doing cartesian products where appropriate. 319 | # 320 | 321 | # Check that limit does work for each value produced by n! 322 | [limit(5,7; range(9))] 323 | null 324 | [0,1,2,3,4,0,1,2,3,4,5,6] 325 | 326 | # Same check for nth 327 | [nth(5,7; range(9;0;-1))] 328 | null 329 | [4,2] 330 | 331 | # Same check for range/3 332 | [range(0,1,2;4,3,2;2,3)] 333 | null 334 | [0,2,0,3,0,2,0,0,0,1,3,1,1,1,1,1,2,2,2,2] 335 | 336 | # Same check for range/1 337 | [range(3,5)] 338 | null 339 | [0,1,2,0,1,2,3,4] 340 | 341 | # Same check for index/1, rindex/1, indices/1 342 | [(index(",","|"), rindex(",","|")), indices(",","|")] 343 | "a,b|c,d,e||f,g,h,|,|,i,j" 344 | [1,3,22,19,[1,5,7,12,14,16,18,20,22],[3,9,10,17,19]] 345 | 346 | # Same check for join/1 347 | join(",","/") 348 | ["a","b","c","d"] 349 | "a,b,c,d" 350 | "a/b/c/d" 351 | 352 | [.[]|join("a")] 353 | [[],[""],["",""],["","",""]] 354 | ["","","a","aa"] 355 | 356 | # Same check for flatten/1 357 | flatten(3,2,1) 358 | [0, [1], [[2]], [[[3]]]] 359 | [0,1,2,3] 360 | [0,1,2,[3]] 361 | [0,1,[2],[[3]]] 362 | 363 | 364 | # 365 | # Slices 366 | # 367 | 368 | [.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]] 369 | [0,1,2,3,4,5,6] 370 | [[], [2,3], [0,1,2,3,4], [5,6], [], []] 371 | 372 | [.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]] 373 | "abcdefghi" 374 | ["","","abcdefg","hi","",""] 375 | 376 | del(.[2:4],.[0],.[-2:]) 377 | [0,1,2,3,4,5,6,7] 378 | [1,4,5] 379 | 380 | .[2:4] = ([], ["a","b"], ["a","b","c"]) 381 | [0,1,2,3,4,5,6,7] 382 | [0,1,4,5,6,7] 383 | [0,1,"a","b",4,5,6,7] 384 | [0,1,"a","b","c",4,5,6,7] 385 | 386 | # Slices at large offsets (issue #1108) 387 | # 388 | # This is written this way because [range()] is 389 | # significantly slower under valgrind than .[] = value. 390 | # 391 | # We range down rather than up so that we have just one realloc. 392 | reduce range(65540;65536;-1) as $i ([]; .[$i] = $i)|.[65536:] 393 | null 394 | [null,65537,65538,65539,65540] 395 | 396 | # 397 | # Variables 398 | # 399 | 400 | 1 as $x | 2 as $y | [$x,$y,$x] 401 | null 402 | [1,2,1] 403 | 404 | [1,2,3][] as $x | [[4,5,6,7][$x]] 405 | null 406 | [5] 407 | [6] 408 | [7] 409 | 410 | 42 as $x | . | . | . + 432 | $x + 1 411 | 34324 412 | 43 413 | 414 | 1 as $x | [$x,$x,$x as $x | $x] 415 | null 416 | [1,1,1] 417 | 418 | [1, {c:3, d:4}] as [$a, {c:$b, b:$c}] | $a, $b, $c 419 | null 420 | 1 421 | 3 422 | null 423 | 424 | . as {as: $kw, "str": $str, ("e"+"x"+"p"): $exp} | [$kw, $str, $exp] 425 | {"as": 1, "str": 2, "exp": 3} 426 | [1, 2, 3] 427 | 428 | .[] as [$a, $b] | [$b, $a] 429 | [[1], [1, 2, 3]] 430 | [null, 1] 431 | [2, 1] 432 | 433 | . as $i | . as [$i] | $i 434 | [0] 435 | 0 436 | 437 | . as [$i] | . as $i | $i 438 | [0] 439 | [0] 440 | 441 | %%FAIL IGNORE MSG 442 | . as [] | null 443 | jq: error: syntax error, unexpected ']', expecting '$' or '[' or '{' (Unix shell quoting issues?) at , line 1: 444 | 445 | %%FAIL IGNORE MSG 446 | . as {} | null 447 | jq: error: syntax error, unexpected '}' (Unix shell quoting issues?) at , line 1: 448 | 449 | # [.,(.[] | {x:.},.),.,.[]] 450 | 451 | # 452 | # Builtin functions 453 | # 454 | 455 | 1+1 456 | null 457 | 2 458 | 459 | 1+1 460 | "wtasdf" 461 | 2.0 462 | 463 | 2-1 464 | null 465 | 1 466 | 467 | 2-(-1) 468 | null 469 | 3 470 | 471 | 1e+0+0.001e3 472 | "I wonder what this will be?" 473 | 20e-1 474 | 475 | .+4 476 | 15 477 | 19.0 478 | 479 | .+null 480 | {"a":42} 481 | {"a":42} 482 | 483 | null+. 484 | null 485 | null 486 | 487 | .a+.b 488 | {"a":42} 489 | 42 490 | 491 | [1,2,3] + [.] 492 | null 493 | [1,2,3,null] 494 | 495 | {"a":1} + {"b":2} + {"c":3} 496 | "asdfasdf" 497 | {"a":1, "b":2, "c":3} 498 | 499 | "asdf" + "jkl;" + . + . + . 500 | "some string" 501 | "asdfjkl;some stringsome stringsome string" 502 | 503 | "\u0000\u0020\u0000" + . 504 | "\u0000\u0020\u0000" 505 | "\u0000 \u0000\u0000 \u0000" 506 | 507 | 42 - . 508 | 11 509 | 31 510 | 511 | [1,2,3,4,1] - [.,3] 512 | 1 513 | [2,4] 514 | 515 | [10 * 20, 20 / .] 516 | 4 517 | [200, 5] 518 | 519 | 1 + 2 * 2 + 10 / 2 520 | null 521 | 10 522 | 523 | [16 / 4 / 2, 16 / 4 * 2, 16 - 4 - 2, 16 - 4 + 2] 524 | null 525 | [2, 8, 10, 14] 526 | 527 | 25 % 7 528 | null 529 | 4 530 | 531 | 49732 % 472 532 | null 533 | 172 534 | 535 | 1 + tonumber + ("10" | tonumber) 536 | 4 537 | 15 538 | 539 | [{"a":42},.object,10,.num,false,true,null,"b",[1,4]] | .[] as $x | [$x == .[]] 540 | {"object": {"a":42}, "num":10.0} 541 | [true, true, false, false, false, false, false, false, false] 542 | [true, true, false, false, false, false, false, false, false] 543 | [false, false, true, true, false, false, false, false, false] 544 | [false, false, true, true, false, false, false, false, false] 545 | [false, false, false, false, true, false, false, false, false] 546 | [false, false, false, false, false, true, false, false, false] 547 | [false, false, false, false, false, false, true, false, false] 548 | [false, false, false, false, false, false, false, true, false] 549 | [false, false, false, false, false, false, false, false, true ] 550 | 551 | [.[] | length] 552 | [[], {}, [1,2], {"a":42}, "asdf", "\u03bc"] 553 | [0, 0, 2, 1, 4, 1] 554 | 555 | utf8bytelength 556 | "asdf\u03bc" 557 | 6 558 | 559 | [.[] | try utf8bytelength catch .] 560 | [[], {}, [1,2], 55, true, false] 561 | ["array ([]) only strings have UTF-8 byte length","object ({}) only strings have UTF-8 byte length","array ([1,2]) only strings have UTF-8 byte length","number (55) only strings have UTF-8 byte length","boolean (true) only strings have UTF-8 byte length","boolean (false) only strings have UTF-8 byte length"] 562 | 563 | 564 | map(keys) 565 | [{}, {"abcd":1,"abc":2,"abcde":3}, {"x":1, "z": 3, "y":2}] 566 | [[], ["abc","abcd","abcde"], ["x","y","z"]] 567 | 568 | [1,2,empty,3,empty,4] 569 | null 570 | [1,2,3,4] 571 | 572 | map(add) 573 | [[], [1,2,3], ["a","b","c"], [[3],[4,5],[6]], [{"a":1}, {"b":2}, {"a":3}]] 574 | [null, 6, "abc", [3,4,5,6], {"a":3, "b": 2}] 575 | 576 | map_values(.+1) 577 | [0,1,2] 578 | [1,2,3] 579 | 580 | # 581 | # User-defined functions 582 | # Oh god. 583 | # 584 | 585 | def f: . + 1; def g: def g: . + 100; f | g | f; (f | g), g 586 | 3.0 587 | 106.0 588 | 105.0 589 | 590 | def f: (1000,2000); f 591 | 123412345 592 | 1000 593 | 2000 594 | 595 | def f(a;b;c;d;e;f): [a+1,b,c,d,e,f]; f(.[0];.[1];.[0];.[0];.[0];.[0]) 596 | [1,2] 597 | [2,2,1,1,1,1] 598 | 599 | # Test precedence of 'def' vs '|' 600 | def a: 0; . | a 601 | null 602 | 0 603 | 604 | # Many arguments 605 | def f(a;b;c;d;e;f;g;h;i;j): [j,i,h,g,f,e,d,c,b,a]; f(.[0];.[1];.[2];.[3];.[4];.[5];.[6];.[7];.[8];.[9]) 606 | [0,1,2,3,4,5,6,7,8,9] 607 | [9,8,7,6,5,4,3,2,1,0] 608 | 609 | ([1,2] + [4,5]) 610 | [1,2,3] 611 | [1,2,4,5] 612 | 613 | true 614 | [1] 615 | true 616 | 617 | null,1,null 618 | "hello" 619 | null 620 | 1 621 | null 622 | 623 | [1,2,3] 624 | [5,6] 625 | [1,2,3] 626 | 627 | [.[]|floor] 628 | [-1.1,1.1,1.9] 629 | [-2, 1, 1] 630 | 631 | [.[]|sqrt] 632 | [4,9] 633 | [2,3] 634 | 635 | (add / length) as $m | map((. - $m) as $d | $d * $d) | add / length | sqrt 636 | [2,4,4,4,5,5,7,9] 637 | 2 638 | 639 | # Should write a test that calls the -lm function from C (or bc(1)) to 640 | # check that they match the corresponding jq functions. However, 641 | # there's so little template code standing between that it suffices to 642 | # test a handful of these. The results were checked by eye against 643 | # bc(1). 644 | atan * 4 * 1000000|floor / 1000000 645 | 1 646 | 3.141592 647 | 648 | [(3.141592 / 2) * (range(0;20) / 20)|cos * 1000000|floor / 1000000] 649 | null 650 | [1,0.996917,0.987688,0.972369,0.951056,0.923879,0.891006,0.85264,0.809017,0.760406,0.707106,0.649448,0.587785,0.522498,0.45399,0.382683,0.309017,0.233445,0.156434,0.078459] 651 | 652 | [(3.141592 / 2) * (range(0;20) / 20)|sin * 1000000|floor / 1000000] 653 | null 654 | [0,0.078459,0.156434,0.233445,0.309016,0.382683,0.45399,0.522498,0.587785,0.649447,0.707106,0.760405,0.809016,0.85264,0.891006,0.923879,0.951056,0.972369,0.987688,0.996917] 655 | 656 | 657 | def f(x): x | x; f([.], . + [42]) 658 | [1,2,3] 659 | [[[1,2,3]]] 660 | [[1,2,3],42] 661 | [[1,2,3,42]] 662 | [1,2,3,42,42] 663 | 664 | # test multiple function arities and redefinition 665 | def f: .+1; def g: f; def f: .+100; def f(a):a+.+11; [(g|f(20)), f] 666 | 1 667 | [33,101] 668 | 669 | # test closures and lexical scoping 670 | def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x) 671 | "more testing" 672 | [1,100,2100.0,100,2100.0] 673 | 674 | # test def f($a) syntax 675 | def x(a;b): a as $a | b as $b | $a + $b; def y($a;$b): $a + $b; def check(a;b): [x(a;b)] == [y(a;b)]; check(.[];.[]*2) 676 | [1,2,3] 677 | true 678 | 679 | # test backtracking through function calls and returns 680 | # this test is *evil* 681 | [[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f] 682 | 999999999 683 | [[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]] 684 | 685 | # test recursion 686 | def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac] 687 | [1,2,3,4] 688 | [1,2,6,24] 689 | 690 | # test stack overflow and reallocation 691 | # this test is disabled for now, it takes a realllllly long time. 692 | # def f: if length > 1000 then . else .+[1]|f end; f | length 693 | # [] 694 | # 1001 695 | 696 | reduce .[] as $x (0; . + $x) 697 | [1,2,4] 698 | 7 699 | 700 | reduce .[] as [$i, {j:$j}] (0; . + $i - $j) 701 | [[2,{"j":1}], [5,{"j":3}], [6,{"j":4}]] 702 | 5 703 | 704 | reduce [[1,2,10], [3,4,10]][] as [$i,$j] (0; . + $i * $j) 705 | null 706 | 14 707 | 708 | # This, while useless, should still compile. 709 | reduce . as $n (.; .) 710 | null 711 | null 712 | 713 | # Destructuring 714 | . as {$a, b: [$c, {$d}]} | [$a, $c, $d] 715 | {"a":1, "b":[2,{"d":3}]} 716 | [1,2,3] 717 | 718 | . as {$a, $b:[$c, $d]}| [$a, $b, $c, $d] 719 | {"a":1, "b":[2,{"d":3}]} 720 | [1,[2,{"d":3}],2,{"d":3}] 721 | 722 | # Destructuring with alternation 723 | .[] | . as {$a, b: [$c, {$d}]} ?// [$a, {$b}, $e] ?// $f | [$a, $b, $c, $d, $e, $f] 724 | [{"a":1, "b":[2,{"d":3}]}, [4, {"b":5, "c":6}, 7, 8, 9], "foo"] 725 | [1, null, 2, 3, null, null] 726 | [4, 5, null, null, 7, null] 727 | [null, null, null, null, null, "foo"] 728 | 729 | .[] | . as {a:$a} ?// {a:$a} ?// $a | $a 730 | [[3],[4],[5],6] 731 | [3] 732 | [4] 733 | [5] 734 | 6 735 | 736 | .[] as {a:$a} ?// {a:$a} ?// $a | $a 737 | [[3],[4],[5],6] 738 | [3] 739 | [4] 740 | [5] 741 | 6 742 | 743 | [[3],[4],[5],6][] | . as {a:$a} ?// {a:$a} ?// $a | $a 744 | null 745 | [3] 746 | [4] 747 | [5] 748 | 6 749 | 750 | [[3],[4],[5],6] | .[] as {a:$a} ?// {a:$a} ?// $a | $a 751 | null 752 | [3] 753 | [4] 754 | [5] 755 | 6 756 | 757 | .[] | . as {a:$a} ?// $a ?// {a:$a} | $a 758 | [[3],[4],[5],6] 759 | [3] 760 | [4] 761 | [5] 762 | 6 763 | 764 | .[] as {a:$a} ?// $a ?// {a:$a} | $a 765 | [[3],[4],[5],6] 766 | [3] 767 | [4] 768 | [5] 769 | 6 770 | 771 | [[3],[4],[5],6][] | . as {a:$a} ?// $a ?// {a:$a} | $a 772 | null 773 | [3] 774 | [4] 775 | [5] 776 | 6 777 | 778 | [[3],[4],[5],6] | .[] as {a:$a} ?// $a ?// {a:$a} | $a 779 | null 780 | [3] 781 | [4] 782 | [5] 783 | 6 784 | 785 | .[] | . as $a ?// {a:$a} ?// {a:$a} | $a 786 | [[3],[4],[5],6] 787 | [3] 788 | [4] 789 | [5] 790 | 6 791 | 792 | .[] as $a ?// {a:$a} ?// {a:$a} | $a 793 | [[3],[4],[5],6] 794 | [3] 795 | [4] 796 | [5] 797 | 6 798 | 799 | [[3],[4],[5],6][] | . as $a ?// {a:$a} ?// {a:$a} | $a 800 | null 801 | [3] 802 | [4] 803 | [5] 804 | 6 805 | 806 | [[3],[4],[5],6] | .[] as $a ?// {a:$a} ?// {a:$a} | $a 807 | null 808 | [3] 809 | [4] 810 | [5] 811 | 6 812 | 813 | . as $dot|any($dot[];not) 814 | [1,2,3,4,true,false,1,2,3,4,5] 815 | true 816 | 817 | . as $dot|any($dot[];not) 818 | [1,2,3,4,true] 819 | false 820 | 821 | . as $dot|all($dot[];.) 822 | [1,2,3,4,true,false,1,2,3,4,5] 823 | false 824 | 825 | . as $dot|all($dot[];.) 826 | [1,2,3,4,true] 827 | true 828 | 829 | # 830 | # Paths 831 | # 832 | 833 | path(.foo[0,1]) 834 | null 835 | ["foo", 0] 836 | ["foo", 1] 837 | 838 | path(.[] | select(.>3)) 839 | [1,5,3] 840 | [1] 841 | 842 | path(.) 843 | 42 844 | [] 845 | 846 | try path(.a | map(select(.b == 0))) catch . 847 | {"a":[{"b":0}]} 848 | "Invalid path expression with result [{\"b\":0}]" 849 | 850 | try path(.a | map(select(.b == 0)) | .[0]) catch . 851 | {"a":[{"b":0}]} 852 | "Invalid path expression near attempt to access element 0 of [{\"b\":0}]" 853 | 854 | try path(.a | map(select(.b == 0)) | .c) catch . 855 | {"a":[{"b":0}]} 856 | "Invalid path expression near attempt to access element \"c\" of [{\"b\":0}]" 857 | 858 | try path(.a | map(select(.b == 0)) | .[]) catch . 859 | {"a":[{"b":0}]} 860 | "Invalid path expression near attempt to iterate through [{\"b\":0}]" 861 | 862 | path(.a[path(.b)[0]]) 863 | {"a":{"b":0}} 864 | ["a","b"] 865 | 866 | [paths] 867 | [1,[[],{"a":2}]] 868 | [[0],[1],[1,0],[1,1],[1,1,"a"]] 869 | 870 | [leaf_paths] 871 | [1,[[],{"a":2}]] 872 | [[0],[1,1,"a"]] 873 | 874 | ["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) 875 | {"bar": 42, "foo": ["a", "b", "c", "d"]} 876 | "b" 877 | {"bar": 42, "foo": ["a", 20, "c", "d"]} 878 | {"bar": 42, "foo": ["a", "c", "d"]} 879 | 880 | map(getpath([2])), map(setpath([2]; 42)), map(delpaths([[2]])) 881 | [[0], [0,1], [0,1,2]] 882 | [null, null, 2] 883 | [[0,null,42], [0,1,42], [0,1,42]] 884 | [[0], [0,1], [0,1]] 885 | 886 | map(delpaths([[0,"foo"]])) 887 | [[{"foo":2, "x":1}], [{"bar":2}]] 888 | [[{"x":1}], [{"bar":2}]] 889 | 890 | ["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) 891 | {"bar":false} 892 | null 893 | {"bar":false, "foo": [null, 20]} 894 | {"bar":false} 895 | 896 | delpaths([[-200]]) 897 | [1,2,3] 898 | [1,2,3] 899 | 900 | try delpaths(0) catch . 901 | {} 902 | "Paths must be specified as an array" 903 | 904 | del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x) 905 | {"foo": [0,1,2,3,4], "bar": [0,1]} 906 | null 907 | {"foo": [0,1,2,3,4], "bar": [0,1]} 908 | {"foo": [1,4], "bar": [1]} 909 | {"bar": [1]} 910 | 911 | del(.[1], .[-6], .[2], .[-3:9]) 912 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 913 | [0, 3, 5, 6, 9] 914 | 915 | # 916 | # Assignment 917 | # 918 | .message = "goodbye" 919 | {"message": "hello"} 920 | {"message": "goodbye"} 921 | 922 | .foo = .bar 923 | {"bar":42} 924 | {"foo":42, "bar":42} 925 | 926 | .foo |= .+1 927 | {"foo": 42} 928 | {"foo": 43} 929 | 930 | .[] += 2, .[] *= 2, .[] -= 2, .[] /= 2, .[] %=2 931 | [1,3,5] 932 | [3,5,7] 933 | [2,6,10] 934 | [-1,1,3] 935 | [0.5, 1.5, 2.5] 936 | [1,1,1] 937 | 938 | [.[] % 7] 939 | [-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7] 940 | [0,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,0] 941 | 942 | .foo += .foo 943 | {"foo":2} 944 | {"foo":4} 945 | 946 | .[0].a |= {"old":., "new":(.+1)} 947 | [{"a":1,"b":2}] 948 | [{"a":{"old":1, "new":2},"b":2}] 949 | 950 | def inc(x): x |= .+1; inc(.[].a) 951 | [{"a":1,"b":2},{"a":2,"b":4},{"a":7,"b":8}] 952 | [{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}] 953 | 954 | # #1358, getpath/1 should work in path expressions 955 | .[] | try (getpath(["a",0,"b"]) |= 5) catch . 956 | [null,{"b":0},{"a":0},{"a":null},{"a":[0,1]},{"a":{"b":1}},{"a":[{}]},{"a":[{"c":3}]}] 957 | {"a":[{"b":5}]} 958 | {"b":0,"a":[{"b":5}]} 959 | "Cannot index number with number" 960 | {"a":[{"b":5}]} 961 | "Cannot index number with string \"b\"" 962 | "Cannot index object with number" 963 | {"a":[{"b":5}]} 964 | {"a":[{"c":3,"b":5}]} 965 | 966 | .[2][3] = 1 967 | [4] 968 | [4, null, [null, null, null, 1]] 969 | 970 | .foo[2].bar = 1 971 | {"foo":[11], "bar":42} 972 | {"foo":[11,null,{"bar":1}], "bar":42} 973 | 974 | try ((map(select(.a == 1))[].b) = 10) catch . 975 | [{"a":0},{"a":1}] 976 | "Invalid path expression near attempt to iterate through [{\"a\":1}]" 977 | 978 | try ((map(select(.a == 1))[].a) |= .+1) catch . 979 | [{"a":0},{"a":1}] 980 | "Invalid path expression near attempt to iterate through [{\"a\":1}]" 981 | 982 | def x: .[1,2]; x=10 983 | [0,1,2] 984 | [0,10,10] 985 | 986 | try (def x: reverse; x=10) catch . 987 | [0,1,2] 988 | "Invalid path expression with result [2,1,0]" 989 | 990 | .[] = 1 991 | [1,null,Infinity,-Infinity,NaN,-NaN] 992 | [1,1,1,1,1,1] 993 | 994 | # 995 | # Conditionals 996 | # 997 | 998 | [.[] | if .foo then "yep" else "nope" end] 999 | [{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}] 1000 | ["yep","yep","yep","yep","nope","nope","yep","nope"] 1001 | 1002 | [.[] | if .baz then "strange" elif .foo then "yep" else "nope" end] 1003 | [{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}] 1004 | ["yep","yep","yep","yep","nope","nope","yep","nope"] 1005 | 1006 | [if 1,null,2 then 3 else 4 end] 1007 | null 1008 | [3,4,3] 1009 | 1010 | [if empty then 3 else 4 end] 1011 | null 1012 | [] 1013 | 1014 | [if 1 then 3,4 else 5 end] 1015 | null 1016 | [3,4] 1017 | 1018 | [if null then 3 else 5,6 end] 1019 | null 1020 | [5,6] 1021 | 1022 | [.[] | [.foo[] // .bar]] 1023 | [{"foo":[1,2], "bar": 42}, {"foo":[1], "bar": null}, {"foo":[null,false,3], "bar": 18}, {"foo":[], "bar":42}, {"foo": [null,false,null], "bar": 41}] 1024 | [[1,2], [1], [3], [42], [41]] 1025 | 1026 | .[] //= .[0] 1027 | ["hello",true,false,[false],null] 1028 | ["hello",true,"hello",[false],"hello"] 1029 | 1030 | .[] | [.[0] and .[1], .[0] or .[1]] 1031 | [[true,[]], [false,1], [42,null], [null,false]] 1032 | [true,true] 1033 | [false,true] 1034 | [false,true] 1035 | [false,false] 1036 | 1037 | [.[] | not] 1038 | [1,0,false,null,true,"hello"] 1039 | [false,false,true,true,false,false] 1040 | 1041 | # Check numeric comparison binops 1042 | [10 > 0, 10 > 10, 10 > 20, 10 < 0, 10 < 10, 10 < 20] 1043 | {} 1044 | [true,false,false,false,false,true] 1045 | 1046 | [10 >= 0, 10 >= 10, 10 >= 20, 10 <= 0, 10 <= 10, 10 <= 20] 1047 | {} 1048 | [true,true,false,false,true,true] 1049 | 1050 | # And some in/equality tests 1051 | [ 10 == 10, 10 != 10, 10 != 11, 10 == 11] 1052 | {} 1053 | [true,false,true,false] 1054 | 1055 | ["hello" == "hello", "hello" != "hello", "hello" == "world", "hello" != "world" ] 1056 | {} 1057 | [true,false,false,true] 1058 | 1059 | [[1,2,3] == [1,2,3], [1,2,3] != [1,2,3], [1,2,3] == [4,5,6], [1,2,3] != [4,5,6]] 1060 | {} 1061 | [true,false,false,true] 1062 | 1063 | [{"foo":42} == {"foo":42},{"foo":42} != {"foo":42}, {"foo":42} != {"bar":42}, {"foo":42} == {"bar":42}] 1064 | {} 1065 | [true,false,true,false] 1066 | 1067 | # ugly complicated thing 1068 | [{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":18},"world"]},{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":19},"world"]}] 1069 | {} 1070 | [true,false] 1071 | 1072 | # containment operator 1073 | [("foo" | contains("foo")), ("foobar" | contains("foo")), ("foo" | contains("foobar"))] 1074 | {} 1075 | [true, true, false] 1076 | 1077 | # Try/catch and general `?` operator 1078 | [.[]|try if . == 0 then error("foo") elif . == 1 then .a elif . == 2 then empty else . end catch .] 1079 | [0,1,2,3] 1080 | ["foo","Cannot index number with string \"a\"",3] 1081 | 1082 | [.[]|(.a, .a)?] 1083 | [null,true,{"a":1}] 1084 | [null,null,1,1] 1085 | 1086 | [[.[]|[.a,.a]]?] 1087 | [null,true,{"a":1}] 1088 | [] 1089 | 1090 | try error("\($__loc__)") catch . 1091 | null 1092 | "{\"file\":\"\",\"line\":1}" 1093 | 1094 | # string operations 1095 | [.[]|startswith("foo")] 1096 | ["fo", "foo", "barfoo", "foobar", "barfoob"] 1097 | [false, true, false, true, false] 1098 | 1099 | [.[]|endswith("foo")] 1100 | ["fo", "foo", "barfoo", "foobar", "barfoob"] 1101 | [false, true, true, false, false] 1102 | 1103 | [.[] | split(", ")] 1104 | ["a,b, c, d, e,f",", a,b, c, d, e,f, "] 1105 | [["a,b","c","d","e,f"],["","a,b","c","d","e,f",""]] 1106 | 1107 | split("") 1108 | "abc" 1109 | ["a","b","c"] 1110 | 1111 | [.[]|ltrimstr("foo")] 1112 | ["fo", "foo", "barfoo", "foobar", "afoo"] 1113 | ["fo","","barfoo","bar","afoo"] 1114 | 1115 | [.[]|rtrimstr("foo")] 1116 | ["fo", "foo", "barfoo", "foobar", "foob"] 1117 | ["fo","","bar","foobar","foob"] 1118 | 1119 | [(index(","), rindex(",")), indices(",")] 1120 | "a,bc,def,ghij,klmno" 1121 | [1,13,[1,4,8,13]] 1122 | 1123 | indices(1) 1124 | [0,1,1,2,3,4,1,5] 1125 | [1,2,6] 1126 | 1127 | indices([1,2]) 1128 | [0,1,2,3,1,4,2,5,1,2,6,7] 1129 | [1,8] 1130 | 1131 | indices([1,2]) 1132 | [1] 1133 | [] 1134 | 1135 | indices(", ") 1136 | "a,b, cd,e, fgh, ijkl" 1137 | [3,9,14] 1138 | 1139 | [.[]|split(",")] 1140 | ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] 1141 | [["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]] 1142 | 1143 | [.[]|split(", ")] 1144 | ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] 1145 | [["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]] 1146 | 1147 | [.[] * 3] 1148 | ["a", "ab", "abc"] 1149 | ["aaa", "ababab", "abcabcabc"] 1150 | 1151 | [.[] / ","] 1152 | ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] 1153 | [["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]] 1154 | 1155 | [.[] / ", "] 1156 | ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] 1157 | [["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]] 1158 | 1159 | map(.[1] as $needle | .[0] | contains($needle)) 1160 | [[[],[]], [[1,2,3], [1,2]], [[1,2,3], [3,1]], [[1,2,3], [4]], [[1,2,3], [1,4]]] 1161 | [true, true, true, false, false] 1162 | 1163 | map(.[1] as $needle | .[0] | contains($needle)) 1164 | [[["foobar", "foobaz"], ["baz", "bar"]], [["foobar", "foobaz"], ["foo"]], [["foobar", "foobaz"], ["blap"]]] 1165 | [true, true, false] 1166 | 1167 | [({foo: 12, bar:13} | contains({foo: 12})), ({foo: 12} | contains({})), ({foo: 12, bar:13} | contains({baz:14}))] 1168 | {} 1169 | [true, true, false] 1170 | 1171 | {foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {}}}) 1172 | {} 1173 | true 1174 | 1175 | {foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {bar: 14}}}) 1176 | {} 1177 | false 1178 | 1179 | sort 1180 | [42,[2,5,3,11],10,{"a":42,"b":2},{"a":42},true,2,[2,6],"hello",null,[2,5,6],{"a":[],"b":1},"abc","ab",[3,10],{},false,"abcd",null] 1181 | [null,null,false,true,2,10,42,"ab","abc","abcd","hello",[2,5,3,11],[2,5,6],[2,6],[3,10],{},{"a":42},{"a":42,"b":2},{"a":[],"b":1}] 1182 | 1183 | (sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2) 1184 | [{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}] 1185 | [{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}] 1186 | [{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}] 1187 | [{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}] 1188 | [[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]] 1189 | [[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]] 1190 | 1191 | unique 1192 | [1,2,5,3,5,3,1,3] 1193 | [1,2,3,5] 1194 | 1195 | unique 1196 | [] 1197 | [] 1198 | 1199 | [min, max, min_by(.[1]), max_by(.[1]), min_by(.[2]), max_by(.[2])] 1200 | [[4,2,"a"],[3,1,"a"],[2,4,"a"],[1,3,"a"]] 1201 | [[1,3,"a"],[4,2,"a"],[3,1,"a"],[2,4,"a"],[4,2,"a"],[1,3,"a"]] 1202 | 1203 | [min,max,min_by(.),max_by(.)] 1204 | [] 1205 | [null,null,null,null] 1206 | 1207 | .foo[.baz] 1208 | {"foo":{"bar":4},"baz":"bar"} 1209 | 4 1210 | 1211 | .[] | .error = "no, it's OK" 1212 | [{"error":true}] 1213 | {"error": "no, it's OK"} 1214 | 1215 | [{a:1}] | .[] | .a=999 1216 | null 1217 | {"a": 999} 1218 | 1219 | to_entries 1220 | {"a": 1, "b": 2} 1221 | [{"key":"a", "value":1}, {"key":"b", "value":2}] 1222 | 1223 | from_entries 1224 | [{"key":"a", "value":1}, {"Key":"b", "Value":2}, {"name":"c", "value":3}, {"Name":"d", "Value":4}] 1225 | {"a": 1, "b": 2, "c": 3, "d": 4} 1226 | 1227 | with_entries(.key |= "KEY_" + .) 1228 | {"a": 1, "b": 2} 1229 | {"KEY_a": 1, "KEY_b": 2} 1230 | 1231 | map(has("foo")) 1232 | [{"foo": 42}, {}] 1233 | [true, false] 1234 | 1235 | map(has(2)) 1236 | [[0,1], ["a","b","c"]] 1237 | [false, true] 1238 | 1239 | keys 1240 | [42,3,35] 1241 | [0,1,2] 1242 | 1243 | [][.] 1244 | 1000000000000000000 1245 | null 1246 | 1247 | map([1,2][0:.]) 1248 | [-1, 1, 2, 3, 1000000000000000000] 1249 | [[1], [1], [1,2], [1,2], [1,2]] 1250 | 1251 | # Test recursive object merge 1252 | 1253 | {"k": {"a": 1, "b": 2}} * . 1254 | {"k": {"a": 0,"c": 3}} 1255 | {"k": {"a": 0, "b": 2, "c": 3}} 1256 | 1257 | {"k": {"a": 1, "b": 2}, "hello": {"x": 1}} * . 1258 | {"k": {"a": 0,"c": 3}, "hello": 1} 1259 | {"k": {"a": 0, "b": 2, "c": 3}, "hello": 1} 1260 | 1261 | {"k": {"a": 1, "b": 2}, "hello": 1} * . 1262 | {"k": {"a": 0,"c": 3}, "hello": {"x": 1}} 1263 | {"k": {"a": 0, "b": 2, "c": 3}, "hello": {"x": 1}} 1264 | 1265 | {"a": {"b": 1}, "c": {"d": 2}, "e": 5} * . 1266 | {"a": {"b": 2}, "c": {"d": 3, "f": 9}} 1267 | {"a": {"b": 2}, "c": {"d": 3, "f": 9}, "e": 5} 1268 | 1269 | [.[]|arrays] 1270 | [1,2,"foo",[],[3,[]],{},true,false,null] 1271 | [[],[3,[]]] 1272 | 1273 | [.[]|objects] 1274 | [1,2,"foo",[],[3,[]],{},true,false,null] 1275 | [{}] 1276 | 1277 | [.[]|iterables] 1278 | [1,2,"foo",[],[3,[]],{},true,false,null] 1279 | [[],[3,[]],{}] 1280 | 1281 | [.[]|scalars] 1282 | [1,2,"foo",[],[3,[]],{},true,false,null] 1283 | [1,2,"foo",true,false,null] 1284 | 1285 | [.[]|values] 1286 | [1,2,"foo",[],[3,[]],{},true,false,null] 1287 | [1,2,"foo",[],[3,[]],{},true,false] 1288 | 1289 | [.[]|booleans] 1290 | [1,2,"foo",[],[3,[]],{},true,false,null] 1291 | [true,false] 1292 | 1293 | [.[]|nulls] 1294 | [1,2,"foo",[],[3,[]],{},true,false,null] 1295 | [null] 1296 | 1297 | flatten 1298 | [0, [1], [[2]], [[[3]]]] 1299 | [0, 1, 2, 3] 1300 | 1301 | flatten(0) 1302 | [0, [1], [[2]], [[[3]]]] 1303 | [0, [1], [[2]], [[[3]]]] 1304 | 1305 | flatten(2) 1306 | [0, [1], [[2]], [[[3]]]] 1307 | [0, 1, 2, [3]] 1308 | 1309 | flatten(2) 1310 | [0, [1, [2]], [1, [[3], 2]]] 1311 | [0, 1, 2, 1, [3], 2] 1312 | 1313 | try flatten(-1) catch . 1314 | [0, [1], [[2]], [[[3]]]] 1315 | "flatten depth must not be negative" 1316 | 1317 | transpose 1318 | [[1], [2,3]] 1319 | [[1,2],[null,3]] 1320 | 1321 | ascii_upcase 1322 | "useful but not for é" 1323 | "USEFUL BUT NOT FOR é" 1324 | 1325 | bsearch(4) 1326 | [1,2,3] 1327 | -4 1328 | 1329 | # strptime tests are in optional.test 1330 | 1331 | strftime("%Y-%m-%dT%H:%M:%SZ") 1332 | [2015,2,5,23,51,47,4,63] 1333 | "2015-03-05T23:51:47Z" 1334 | 1335 | strftime("%A, %B %d, %Y") 1336 | 1435677542.822351 1337 | "Tuesday, June 30, 2015" 1338 | 1339 | gmtime 1340 | 1425599507 1341 | [2015,2,5,23,51,47,4,63] 1342 | 1343 | # module system 1344 | import "a" as foo; import "b" as bar; def fooa: foo::a; [fooa, bar::a, bar::b, foo::a] 1345 | null 1346 | ["a","b","c","a"] 1347 | 1348 | import "c" as foo; [foo::a, foo::c] 1349 | null 1350 | [0,"acmehbah"] 1351 | 1352 | include "c"; [a, c] 1353 | null 1354 | [0,"acmehbah"] 1355 | 1356 | %%FAIL 1357 | module (.+1); 0 1358 | jq: error: Module metadata must be constant at , line 1: 1359 | 1360 | %%FAIL 1361 | include "a" (.+1); 0 1362 | jq: error: Module metadata must be constant at , line 1: 1363 | 1364 | %%FAIL 1365 | include "a" []; 0 1366 | jq: error: Module metadata must be an object at , line 1: 1367 | 1368 | %%FAIL 1369 | include "\ "; 0 1370 | jq: error: Invalid escape at line 1, column 4 (while parsing '"\ "') at , line 1: 1371 | 1372 | %%FAIL 1373 | include "\(a)"; 0 1374 | jq: error: Import path must be constant at , line 1: 1375 | 1376 | modulemeta 1377 | "c" 1378 | {"whatever":null,"deps":[{"as":"foo","is_data":false,"relpath":"a"},{"search":"./","as":"d","is_data":false,"relpath":"d"},{"search":"./","as":"d2","is_data":false,"relpath":"d"},{"search":"./../lib/jq","as":"e","is_data":false,"relpath":"e"},{"search":"./../lib/jq","as":"f","is_data":false,"relpath":"f"},{"as":"d","is_data":true,"relpath":"data"}]} 1379 | 1380 | %%FAIL IGNORE MSG 1381 | import "syntaxerror" as e; . 1382 | jq: error: syntax error, unexpected ';', expecting $end (Unix shell quoting issues?) at /home/nico/ws/jq/tests/modules/syntaxerror/syntaxerror.jq, line 1: 1383 | 1384 | %%FAIL IGNORE MSG 1385 | %::wat 1386 | jq: error: syntax error, unexpected '%', expecting $end (Unix shell quoting issues?) at , line 1: 1387 | 1388 | import "test_bind_order" as check; check::check 1389 | null 1390 | true 1391 | 1392 | try -. catch . 1393 | "very-long-string" 1394 | "string (\"very-long-...) cannot be negated" 1395 | 1396 | join(",") 1397 | ["1",2,true,false,3.4] 1398 | "1,2,true,false,3.4" 1399 | 1400 | .[] | join(",") 1401 | [[], [null], [null,null], [null,null,null]] 1402 | "" 1403 | "" 1404 | "," 1405 | ",," 1406 | 1407 | .[] | join(",") 1408 | [["a",null], [null,"a"]] 1409 | "a," 1410 | ",a" 1411 | 1412 | try join(",") catch . 1413 | ["1","2",{"a":{"b":{"c":33}}}] 1414 | "string (\"1,2,\") and object ({\"a\":{\"b\":{...) cannot be added" 1415 | 1416 | try join(",") catch . 1417 | ["1","2",[3,4,5]] 1418 | "string (\"1,2,\") and array ([3,4,5]) cannot be added" 1419 | 1420 | {if:0,and:1,or:2,then:3,else:4,elif:5,end:6,as:7,def:8,reduce:9,foreach:10,try:11,catch:12,label:13,import:14,include:15,module:16} 1421 | null 1422 | {"if":0,"and":1,"or":2,"then":3,"else":4,"elif":5,"end":6,"as":7,"def":8,"reduce":9,"foreach":10,"try":11,"catch":12,"label":13,"import":14,"include":15,"module":16} 1423 | 1424 | try (1/.) catch . 1425 | 0 1426 | "number (1) and number (0) cannot be divided because the divisor is zero" 1427 | 1428 | try (1%.) catch . 1429 | 0 1430 | "number (1) and number (0) cannot be divided (remainder) because the divisor is zero" 1431 | 1432 | %%FAIL 1433 | 1/0 1434 | jq: error: Division by zero? at , line 1: 1435 | 1436 | # Basic numbers tests: integers, powers of two 1437 | [range(-52;52;1)] as $powers | [$powers[]|pow(2;.)|log2] == $powers 1438 | null 1439 | true 1440 | 1441 | [range(-99/2;99/2;1)] as $orig | [$orig[]|pow(2;.)|log2] as $back | ($orig|keys)[]|. as $k | (($orig|.[$k])-($back|.[$k]))|if . < 0 then . * -1 else . end|select(.>.00005) 1442 | null 1443 | 1444 | %%FAIL IGNORE MSG 1445 | } 1446 | jq: error: syntax error, unexpected INVALID_CHARACTER, expecting $end (Unix shell quoting issues?) at , line 1: 1447 | 1448 | (.[{}] = 0)? 1449 | null 1450 | 1451 | INDEX(range(5)|[., "foo\(.)"]; .[0]) 1452 | null 1453 | {"0":[0,"foo0"],"1":[1,"foo1"],"2":[2,"foo2"],"3":[3,"foo3"],"4":[4,"foo4"]} 1454 | 1455 | JOIN({"0":[0,"abc"],"1":[1,"bcd"],"2":[2,"def"],"3":[3,"efg"],"4":[4,"fgh"]}; .[0]|tostring) 1456 | [[5,"foo"],[3,"bar"],[1,"foobar"]] 1457 | [[[5,"foo"],null],[[3,"bar"],[3,"efg"]],[[1,"foobar"],[1,"bcd"]]] 1458 | 1459 | range(5;10)|IN(range(10)) 1460 | null 1461 | true 1462 | true 1463 | true 1464 | true 1465 | true 1466 | 1467 | range(10;12)|IN(range(10)) 1468 | null 1469 | false 1470 | false 1471 | 1472 | IN(range(10;20); range(10)) 1473 | null 1474 | false 1475 | 1476 | IN(range(5;20); range(10)) 1477 | null 1478 | true 1479 | 1480 | # Regression test for #1347 1481 | (.a as $x | .b) = "b" 1482 | {"a":null,"b":null} 1483 | {"a":null,"b":"b"} 1484 | 1485 | # Regression test for #1368 1486 | (.. | select(type == "object" and has("b") and (.b | type) == "array")|.b) |= .[0] 1487 | {"a": {"b": [1, {"b": 3}]}} 1488 | {"a": {"b": 1}} 1489 | 1490 | isempty(empty) 1491 | null 1492 | true 1493 | 1494 | isempty(range(3)) 1495 | null 1496 | false 1497 | 1498 | isempty(1,error("foo")) 1499 | null 1500 | false 1501 | 1502 | 1503 | -------------------------------------------------------------------------------- /src/test/resources/logging.properties: -------------------------------------------------------------------------------- 1 | handlers=java.util.logging.ConsoleHandler 2 | .level=FINE 3 | -------------------------------------------------------------------------------- /src/test/resources/modules/.jq: -------------------------------------------------------------------------------- 1 | def foo: "baz"; 2 | def f: "wat"; 3 | def f: "foo"; 4 | def g: "bar"; 5 | def fg: f+g; 6 | -------------------------------------------------------------------------------- /src/test/resources/modules/a.jq: -------------------------------------------------------------------------------- 1 | module {version:1.7}; 2 | def a: "a"; 3 | -------------------------------------------------------------------------------- /src/test/resources/modules/b/b.jq: -------------------------------------------------------------------------------- 1 | def a: "b"; 2 | def b: "c"; 3 | -------------------------------------------------------------------------------- /src/test/resources/modules/c/c.jq: -------------------------------------------------------------------------------- 1 | module {whatever:null}; 2 | import "a" as foo; 3 | import "d" as d {search:"./"}; 4 | import "d" as d2{search:"./"}; 5 | import "e" as e {search:"./../lib/jq"}; 6 | import "f" as f {search:"./../lib/jq"}; 7 | import "data" as $d; 8 | def a: 0; 9 | def c: 10 | if $d::d[0] != {this:"is a test",that:"is too"} then error("data import is busted") 11 | elif d2::meh != d::meh then error("import twice doesn't work") 12 | elif foo::a != "a" then error("foo::a didn't work as expected") 13 | elif d::meh != "meh" then error("d::meh didn't work as expected") 14 | elif e::bah != "bah" then error("e::bah didn't work as expected") 15 | elif f::f != "f is here" then error("f::f didn't work as expected") 16 | else foo::a + "c" + d::meh + e::bah end; 17 | -------------------------------------------------------------------------------- /src/test/resources/modules/c/d.jq: -------------------------------------------------------------------------------- 1 | def meh: "meh"; 2 | -------------------------------------------------------------------------------- /src/test/resources/modules/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "this": "is a test", 3 | "that": "is too" 4 | } 5 | -------------------------------------------------------------------------------- /src/test/resources/modules/lib/jq/e/e.jq: -------------------------------------------------------------------------------- 1 | def bah: "bah"; 2 | -------------------------------------------------------------------------------- /src/test/resources/modules/lib/jq/f.jq: -------------------------------------------------------------------------------- 1 | def f: "f is here"; 2 | -------------------------------------------------------------------------------- /src/test/resources/modules/syntaxerror/syntaxerror.jq: -------------------------------------------------------------------------------- 1 | wat; 2 | -------------------------------------------------------------------------------- /src/test/resources/modules/test_bind_order.jq: -------------------------------------------------------------------------------- 1 | import "test_bind_order0" as t; 2 | import "test_bind_order1" as t; 3 | import "test_bind_order2" as t; 4 | def check: if [t::sym0,t::sym1,t::sym2] == [0,1,2] then true else false end; 5 | -------------------------------------------------------------------------------- /src/test/resources/modules/test_bind_order0.jq: -------------------------------------------------------------------------------- 1 | def sym0: 0; 2 | -------------------------------------------------------------------------------- /src/test/resources/modules/test_bind_order1.jq: -------------------------------------------------------------------------------- 1 | def sym0: 1; 2 | def sym1: 1; 3 | -------------------------------------------------------------------------------- /src/test/resources/modules/test_bind_order2.jq: -------------------------------------------------------------------------------- 1 | def sym1: 2; 2 | def sym2: 2; 3 | -------------------------------------------------------------------------------- /src/test/resources/onig.test: -------------------------------------------------------------------------------- 1 | # match builtin 2 | [match("( )*"; "g")] 3 | "abc" 4 | [{"offset":0, "length":0, "string":"", "captures":[]},{"offset":1, "length":0, "string":"", "captures":[]},{"offset":2, "length":0, "string":"", "captures":[]}] 5 | 6 | [match("( )*"; "gn")] 7 | "abc" 8 | [] 9 | 10 | [match("a"; "gi")] 11 | "āáàä" 12 | [] 13 | 14 | [match(["(bar)"])] 15 | "foo bar" 16 | [{"offset": 4, "length": 3, "string": "bar", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": null}]}] 17 | 18 | # offsets account for combining codepoints and multi-byte UTF-8 19 | [match("bar")] 20 | "ā bar with a combining codepoint U+0304" 21 | [{"offset": 3, "length": 3, "string": "bar", "captures":[]}] 22 | 23 | # matches with combining codepoints still count them in their length 24 | [match("bār")] 25 | "a bār" 26 | [{"offset": 2, "length": 4, "string": "bār", "captures":[]}] 27 | 28 | [match(".+?\\b")] 29 | "ā two-codepoint grapheme" 30 | [{"offset": 0, "length": 2, "string": "ā", "captures":[]}] 31 | 32 | [match(["foo (?bar)? foo", "ig"])] 33 | "foo bar foo foo foo" 34 | [{"offset": 0, "length": 11, "string": "foo bar foo", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]},{"offset":12, "length": 8, "string": "foo foo", "captures":[{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}] 35 | 36 | #test builtin 37 | [test("( )*"; "gn")] 38 | "abc" 39 | [false] 40 | 41 | [test("ā")] 42 | "ā" 43 | [true] 44 | 45 | capture("(?[a-z]+)-(?[0-9]+)") 46 | "xyzzy-14" 47 | {"a":"xyzzy","n":"14"} 48 | 49 | 50 | # jq-coded utilities built on match: 51 | # 52 | # The second element in these tests' inputs tests the case where the 53 | # fromstring matches both the head and tail of the string 54 | [.[] | sub(", "; ":")] 55 | ["a,b, c, d, e,f", ", a,b, c, d, e,f, "] 56 | ["a,b:c, d, e,f",":a,b, c, d, e,f, "] 57 | 58 | sub("^(?.)"; "Head=\(.head) Tail=") 59 | "abcdef" 60 | "Head=a Tail=bcdef" 61 | 62 | [.[] | gsub(", "; ":")] 63 | ["a,b, c, d, e,f",", a,b, c, d, e,f, "] 64 | ["a,b:c:d:e,f",":a,b:c:d:e,f:"] 65 | 66 | gsub("(?\\d)"; ":\(.d);") 67 | "a1b2" 68 | "a:1;b:2;" 69 | 70 | gsub("a";"b") 71 | "aaaaa" 72 | "bbbbb" 73 | 74 | gsub( "(.*)"; ""; "x") 75 | "" 76 | "" 77 | 78 | [.[] | scan(", ")] 79 | ["a,b, c, d, e,f",", a,b, c, d, e,f, "] 80 | [", ",", ",", ",", ",", ",", ",", ",", "] 81 | 82 | [.[]|[[sub(", *";":")], [gsub(", *";":")], [scan(", *")]]] 83 | ["a,b, c, d, e,f",", a,b, c, d, e,f, "] 84 | [[["a:b, c, d, e,f"],["a:b:c:d:e:f"],[",",", ",", ",", ",","]],[[":a,b, c, d, e,f, "],[":a:b:c:d:e:f:"],[", ",",",", ",", ",", ",",",", "]]] 85 | 86 | [.[]|[[sub(", +";":")], [gsub(", +";":")], [scan(", +")]]] 87 | ["a,b, c, d, e,f",", a,b, c, d, e,f, "] 88 | [[["a,b:c, d, e,f"],["a,b:c:d:e,f"],[", ",", ",", "]],[[":a,b, c, d, e,f, "],[":a,b:c:d:e,f:"],[", ",", ",", ",", ",", "]]] 89 | 90 | # reference to named captures 91 | gsub("(?.)[^a]*"; "+\(.x)-") 92 | "Abcabc" 93 | "+A-+a-" 94 | 95 | # utf-8 96 | sub("(?.)"; "\(.x)!") 97 | "’" 98 | "’!" 99 | -------------------------------------------------------------------------------- /src/test/resources/optional.test: -------------------------------------------------------------------------------- 1 | # See tests/jq.test and the jq manual for more information. 2 | 3 | # strptime() is not available on mingw/WIN32 4 | [strptime("%Y-%m-%dT%H:%M:%SZ")|(.,mktime)] 5 | "2015-03-05T23:51:47Z" 6 | [[2015,2,5,23,51,47,4,63],1425599507] 7 | 8 | # Check day-of-week and day of year computations 9 | # (should trip an assert if this fails) 10 | # This date range 11 | last(range(365 * 67)|("1970-03-01T01:02:03Z"|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime) + (86400 * .)|strftime("%Y-%m-%dT%H:%M:%SZ")|strptime("%Y-%m-%dT%H:%M:%SZ")) 12 | null 13 | [2037,1,11,1,2,3,3,41] 14 | 15 | # %e is not available on mingw/WIN32 16 | strftime("%A, %B %e, %Y") 17 | 1435677542.822351 18 | "Tuesday, June 30, 2015" 19 | 20 | 21 | --------------------------------------------------------------------------------