├── .github └── dependabot.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main ├── java │ └── com │ │ └── bertramlabs │ │ └── plugins │ │ └── hcl4j │ │ ├── HCLBaseDataLookups.java │ │ ├── HCLBaseFunctions.java │ │ ├── HCLDataLookup.java │ │ ├── HCLFunction.java │ │ ├── HCLParser.java │ │ ├── HCLParserException.java │ │ ├── RuntimeSymbols │ │ ├── AnyPrimitiveType.java │ │ ├── BooleanPrimitiveType.java │ │ ├── ComputedObject.java │ │ ├── ComputedTuple.java │ │ ├── EvalSymbol.java │ │ ├── ForConditional.java │ │ ├── ForSource.java │ │ ├── Function.java │ │ ├── GroupedExpression.java │ │ ├── ListExpr.java │ │ ├── ListPrimitiveType.java │ │ ├── MapPrimitiveType.java │ │ ├── NumberPrimitiveType.java │ │ ├── Operator.java │ │ ├── PrimitiveType.java │ │ ├── SetPrimitiveType.java │ │ ├── StringInterpolatedExpression.java │ │ ├── StringPrimitiveType.java │ │ ├── SubTypePrimitiveType.java │ │ ├── Variable.java │ │ └── VariableTree.java │ │ ├── symbols │ │ ├── GenericSymbol.java │ │ ├── HCLArray.java │ │ ├── HCLAttribute.java │ │ ├── HCLBlock.java │ │ ├── HCLMap.java │ │ ├── HCLValue.java │ │ └── Symbol.java │ │ └── utils │ │ ├── HttpApiClient.java │ │ └── ServiceResponse.java └── jflex │ └── com │ └── bertramlabs │ └── plugins │ └── hcl4j │ └── HCLLexer.jflex └── test └── groovy └── com └── bertramlabs └── plugins └── hcl4j ├── HCLBaseDataLookupsSpec.groovy ├── HCLLexerSpec.groovy └── HCLParserSpec.groovy /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: "gradle" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .gradle/ 3 | *.iws 4 | *.ipr 5 | .idea 6 | .DS_Store 7 | *.iml 8 | gradle.properties 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HCL4j 2 | ===== 3 | 4 | HCL4j is a Parser for the Hashicorp Configuration Language on the JVM. This provides a mechanism for converting HCL syntax into an Object Map that can be used for further inspection. 5 | 6 | Features: 7 | 8 | * Support for Syntax parsing 9 | * Nested Array and Map support 10 | * Runtime Parsing Evaluation (Variable Traversal, Function calls, mathematical operations, conditional expressions, for loop objects and tuples) 11 | 12 | 13 | ## Installation 14 | 15 | Using gradle one can include the hcl4j dependency like so: 16 | 17 | ```groovy 18 | dependencies { 19 | compile "com.bertramlabs.plugins:hcl4j:0.9.4" 20 | } 21 | ``` 22 | 23 | ## What's New 24 | 25 | * **0.9.8** Fix NPE in startswith condition if no default is provided 26 | * **0.9.7** Fixed issue with ?: conditional expression didnt have whitespace 27 | * **0.9.6** Function result accessor with . or [] 28 | * **0.9.5** Adding support for regexall method 29 | * **0.9.4** Fixing concurrent modification exception on data lookup 30 | * **0.9.3** Added missing methods for array manipulation such as flatten and try 31 | * **0.9.1** Remove annoying Nested Map Debug Log 32 | * **0.9.0** HCL Tuple for loop nested improvements. String escapes fixed. anytrue and alltrue methods added. 33 | * **0.8.0** HCL For Loop Tuples now evaluated. 34 | * **0.7.7** HCL Periods in attribute names referenced from Unicodes ID_Continue, fixed. 35 | * **0.7.6** SLF4j 1.7.36 upgrade to reduce CVE's 36 | * **0.7.5** For Tuple with boolean conditionals as value expression did not work before 37 | * **0.7.4** Fixing Array Access via tree traversal i.e. local.my_array.0 vs local.my_array[0] 38 | * **0.7.3** Fixing Multiline Formatting Issue 39 | * **0.7.2** Handling nested Block types in a List as a non null value 40 | * **0.7.1** Adding some parser exception safety and added slf4j dependency for logging errors 41 | * **0.7.0** Adding some null safety on some of the base functions 42 | * **0.6.9** Fixed some null pointer issues if content on base64encode was null and some exceptions on array traversal 43 | * **0.6.8** Fixed ! (not) operator and added base64encode, base64decode, textdecodebase64, textencodebase64 44 | * **0.6.7** Fixed operators called on variables without spaces 45 | * **0.6.6** Fixed nested strings inside an interpolation syntax as well as some add conditional checks 46 | * **0.6.5** Fixed issue with same line comments sometimes causing a parser error as well as Maps with new line terminators causing issues 47 | * **0.6.4** Improved toString() behavior of VariableTree when serializing to JSON 48 | * **0.6.3** Updated Gradle Dependencies to most recent versions. Fixed issue with accessing local vars from multiple `locals{}` blocks in same context. Function calls with non evaluated arguments are no longer processed for safety. 49 | * **0.6.2** Handling class cast exception on contains in some scenarios and alias subType on primitive types to check children 50 | * **0.6.1** Handling (any) object sub type primitive. Handling non quote enclosed block attributes. Added jsonencode and jsondecode function implementations. 51 | * **0.6.0** Runtime parsing of Conditional Expressions, Mathematical Operators, String Interpolation now supported. Added additional Terraform Base Functions. Improved parser syntax to handle some outliar formats. Nested Type Primitives now work more completely (subType no longer used, check children of the Primitive Symbol) 52 | * **0.5.0** Runtime Parsing is now supported and for loops. There are huge improvements to support actually evaluating complex runtime operations during the parsing of the hcl. variables can even be processed via the parseVars method. 53 | * **0.4.0** Primitive Types are now appended into the Map. These are of an extended `PrimitiveType` class. These Types include `StringPrimitiveType`, `NumberPrimitiveType`, `BooleanPrimitiveType`, `MapPrimitiveType`, and lastly `ListPrimitiveType` with a `subType` capable property. 54 | 55 | ## Usage 56 | 57 | Using the HCL Parser is fairly straightfoward. Most calls are still limited to use of the `HCLParser` class itself. There are several `parse` method helpers supporting both `File`, `InputStream`, `String`, and `Reader` as inputs. 58 | 59 | 60 | ```java 61 | import com.bertramlabs.plugins.hcl4j.HCLParser; 62 | 63 | File terraformFile = new File("terraform.tf"); 64 | Map results = new HCLParser().parse(terraformFile, "UTF-8"); 65 | ``` 66 | 67 | For More Information on the HCL Syntax Please see the project page: 68 | 69 | [https://github.com/hashicorp/hcl](https://github.com/hashicorp/hcl) 70 | 71 | 72 | ## Things to be Done 73 | 74 | * for loop objects are processed but not evaluated 75 | * add more method definitions for base terraform functions 76 | * add handlers for data lookup modules 77 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenLocal() 4 | mavenCentral() 5 | maven { 6 | url "https://xbib.org/repository" 7 | } 8 | maven { url "https://plugins.gradle.org/m2/" } 9 | } 10 | dependencies { 11 | classpath 'org.xbib.gradle.plugin:gradle-plugin-jflex:3.0.2' 12 | classpath "io.github.gradle-nexus:publish-plugin:1.3.0" 13 | } 14 | } 15 | 16 | 17 | 18 | apply plugin: 'java' 19 | apply plugin: 'java-library' 20 | apply plugin: 'groovy' 21 | apply plugin: 'maven-publish' 22 | apply plugin: 'idea' 23 | apply plugin: 'signing' 24 | apply plugin: 'org.xbib.gradle.plugin.jflex' 25 | 26 | ext { 27 | isBuildSnapshot = version.endsWith('-SNAPSHOT') 28 | isReleaseVersion = !isBuildSnapshot 29 | } 30 | 31 | 32 | group = 'com.bertramlabs.plugins' 33 | version = '0.9.9' 34 | 35 | ext.isReleaseVersion = !version.endsWith("SNAPSHOT") 36 | sourceCompatibility = "1.8" 37 | targetCompatibility = "1.8" 38 | 39 | java { 40 | sourceCompatibility = JavaVersion.toVersion("8") 41 | targetCompatibility = JavaVersion.toVersion("8") 42 | withSourcesJar() 43 | withJavadocJar() 44 | } 45 | repositories { 46 | mavenLocal() 47 | mavenCentral() 48 | } 49 | 50 | dependencies { 51 | api "commons-logging:commons-logging:1.3.1" 52 | api 'com.fasterxml.jackson.core:jackson-databind:2.18.1' 53 | api "commons-logging:commons-logging:1.3.3" 54 | api 'com.fasterxml.jackson.core:jackson-databind:2.18.1' 55 | api 'com.fasterxml.jackson.core:jackson-annotations:2.17.1' 56 | api 'com.fasterxml.jackson.core:jackson-core:2.18.1' 57 | api "org.slf4j:slf4j-api:2.0.16" 58 | api 'org.apache.httpcomponents:httpclient:4.5.14' 59 | api 'org.apache.httpcomponents:httpcore:4.4.16' 60 | api 'org.apache.httpcomponents:httpmime:4.5.14' 61 | api group: 'commons-beanutils', name: 'commons-beanutils', version: '1.9.4' 62 | api 'commons-net:commons-net:3.11.1' 63 | testImplementation platform("org.spockframework:spock-bom:2.3-groovy-4.0") 64 | testImplementation "org.spockframework:spock-core" 65 | testImplementation "org.spockframework:spock-junit4" // you can remove this if your code does not rely on old JUnit 4 rules 66 | testImplementation "org.apache.groovy:groovy-all:4.0.24" 67 | 68 | 69 | // testImplementation "org.spockframework:spock-core:2.3-groovy-3.0" 70 | } 71 | 72 | if (isReleaseVersion) { 73 | apply plugin: "io.github.gradle-nexus.publish-plugin" 74 | nexusPublishing { 75 | repositories { 76 | sonatype { 77 | if(project.hasProperty('mavenUser')) { 78 | username = mavenUser 79 | password = mavenPassword 80 | } 81 | } 82 | } 83 | } 84 | } else { 85 | 86 | } 87 | 88 | publishing { 89 | publications { 90 | maven(MavenPublication) { 91 | artifactId 'hcl4j' 92 | pom.withXml { 93 | asNode().children().last() + { 94 | resolveStrategy = Closure.DELEGATE_FIRST 95 | name 'hcl4j' 96 | description 'Hashicorp Configuration Language (HCL) Java Parser' 97 | url 'https://github.com/bertramdev/hcl4j' 98 | scm { 99 | url 'https://github.com/bertramdev/hcl4j' 100 | connection 'scm:https://bertramdev@github.com/bertramdev/hcl4j.git' 101 | developerConnection 'scm:git://github.com/bertramdev/hcl4j.git' 102 | } 103 | licenses { 104 | license { 105 | name 'The Apache Software License, Version 2.0' 106 | url 'http://www.apache.org/license/LICENSE-2.0.txt' 107 | distribution 'repo' 108 | } 109 | } 110 | developers { 111 | developer { 112 | id 'davydotcom' 113 | name 'David Estes' 114 | email 'davydotcom@gmail.com' 115 | } 116 | } 117 | } 118 | } 119 | from components.java 120 | 121 | } 122 | } 123 | } 124 | 125 | 126 | //Define bintrayUser and bintrayKey in ~/.gradle/gradle.properties 127 | // bintray { 128 | // if(project.hasProperty('bintrayUser')) { 129 | // user = bintrayUser 130 | // key = bintrayKey 131 | // } 132 | // publications = ['maven'] 133 | // pkg { 134 | // repo = 'gomorpheus' 135 | // userOrg = 'bertramlabs' 136 | // name = 'hcl4j' 137 | // vcsUrl = 'https://github.com/bertramdev/hcl4j.git' 138 | // licenses = ['Apache-2.0'] 139 | // } 140 | // } 141 | 142 | 143 | 144 | // task javadocJar(type: Jar, dependsOn: javadoc) { 145 | // archiveClassifier = 'javadoc' 146 | // from 'build/docs/javadoc' 147 | // } 148 | 149 | // task sourcesJar(type: Jar, dependsOn: jflex) { 150 | // archiveClassifier = 'sources' 151 | // from sourceSets.main.allSource 152 | // } 153 | 154 | task morpheusJavaDoc(type: Javadoc) { 155 | source = sourceSets.main.allJava 156 | title = "Hcl4j Docs" 157 | } 158 | 159 | task(console, dependsOn: 'classes', type: JavaExec) { 160 | main = 'groovy.ui.Console' 161 | classpath = sourceSets.main.runtimeClasspath 162 | } 163 | 164 | test { 165 | useJUnitPlatform() 166 | testLogging { 167 | exceptionFormat = 'full' 168 | showStandardStreams = true 169 | } 170 | } 171 | 172 | sourcesJar.dependsOn('generateJflex') 173 | 174 | afterEvaluate { 175 | 176 | 177 | signing { 178 | required { isReleaseVersion && gradle.taskGraph.hasTask("publish") } 179 | sign publishing.publications.maven 180 | } 181 | } 182 | 183 | tasks.withType(Sign) { 184 | onlyIf { isReleaseVersion } 185 | } 186 | 187 | //do not generate extra load on Nexus with new staging repository if signing fails 188 | tasks.withType(io.github.gradlenexus.publishplugin.InitializeNexusStagingRepository).configureEach { 189 | shouldRunAfter(tasks.withType(Sign)) 190 | } 191 | 192 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wondrify/hcl4j/25419100079727b85f7c00e767c582da81c28de9/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.6-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. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 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. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 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 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/HCLBaseDataLookups.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j; 2 | 3 | import com.bertramlabs.plugins.hcl4j.utils.HttpApiClient; 4 | import com.bertramlabs.plugins.hcl4j.utils.ServiceResponse; 5 | 6 | import java.net.URISyntaxException; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Base64; 9 | import java.util.LinkedHashMap; 10 | import java.util.Map; 11 | 12 | public class HCLBaseDataLookups { 13 | static void registerBaseFunctions(HCLParser parser) { 14 | parser.registerDataLookup("http",(properties) -> { 15 | LinkedHashMap schema = new LinkedHashMap<>(properties); 16 | HttpApiClient client = new HttpApiClient(); 17 | try { 18 | String method = (String)(properties.get("method")); 19 | if(method == null) { 20 | method = "GET"; //default GET Method 21 | } 22 | HttpApiClient.RequestOptions requestOptions = new HttpApiClient.RequestOptions(); 23 | requestOptions.body = properties.get("request_body"); 24 | requestOptions.headers = (Map)(properties.get("request_headers") ); 25 | requestOptions.ignoreSSL = properties.get("insecure") != null && (Boolean) (properties.get("insecure")); 26 | if(properties.get("request_timeout_ms")!=null) { 27 | requestOptions.timeout = Integer.parseInt((String)(properties.get("request_timeout_ms"))); 28 | } 29 | ServiceResponse response = client.callApi((String)(properties.get("url")),null,null,null,requestOptions,method); 30 | 31 | schema.put("id",properties.get("url")); 32 | schema.put("status_code",Integer.parseInt(response.getErrorCode())); 33 | schema.put("body",response.getContent()); 34 | String body = response.getContent(); 35 | if(body != null) { 36 | String encodedBody = Base64.getEncoder().encodeToString(body.getBytes(StandardCharsets.UTF_8)); 37 | schema.put("response_body_base64",encodedBody); 38 | } 39 | schema.put("response_headers",response.getHeaders()); 40 | schema.put("response_body",response.getContent()); 41 | } catch (URISyntaxException e) { 42 | throw new RuntimeException(e); 43 | } catch (Exception e2) { 44 | throw new RuntimeException(e2); 45 | } finally { 46 | client.shutdownClient(); 47 | } 48 | return schema; 49 | }); 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/HCLBaseFunctions.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.core.type.TypeReference; 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.node.JsonNodeType; 8 | 9 | import java.nio.charset.Charset; 10 | import java.nio.charset.StandardCharsets; 11 | import java.text.SimpleDateFormat; 12 | import java.util.*; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | * Provides implementations of the common Terraform Base HCL Functions that are commonly used. 18 | * Functions can easily be registered dynamically on the HCLParser as well but this particular set is auto registered in the 19 | * constructor of the {@link HCLParser} 20 | * TODO: Not all functions have been implemented yet. Most functions currently fail by returning null to prevent errors from occurring 21 | * during high level processing. This could be changed in the future to be an optional behavior of the parser 22 | * 23 | * @since 0.5.0 24 | * @author David Estes 25 | */ 26 | public class HCLBaseFunctions { 27 | static void registerBaseFunctions(HCLParser parser) { 28 | parser.registerFunction("upper", (arguments) -> { 29 | if(arguments.size() > 0 && arguments.get(0) != null) { 30 | return arguments.get(0).toString().toUpperCase(Locale.ROOT); 31 | } 32 | return null; 33 | }); 34 | 35 | parser.registerFunction("lower", (arguments) -> { 36 | if(arguments.size() > 0 && arguments.get(0) != null) { 37 | return arguments.get(0).toString().toLowerCase(Locale.ROOT); 38 | } 39 | return null; 40 | }); 41 | 42 | //TODO: Implement this function 43 | // parser.registerFunction("can", (arguments) -> { 44 | // if(arguments.size() > 0 && arguments.get(0) != null) { 45 | // return arguments.get(0) != null; 46 | // } 47 | // return false; 48 | // }); 49 | 50 | parser.registerFunction("format", (arguments) -> { 51 | if(arguments.size() > 0 && arguments.get(0) != null) { 52 | String val = arguments.get(0).toString(); 53 | ArrayList args = new ArrayList<>(); 54 | for(int x=1;x { 64 | if(arguments.size() > 0 && arguments.get(0) != null) { 65 | return arguments.get(0).toString(); 66 | } 67 | return null; 68 | }); 69 | 70 | 71 | parser.registerFunction("trim", (arguments) -> { 72 | if(arguments.size() > 0 && arguments.get(0) != null) { 73 | return arguments.get(0).toString(); 74 | } 75 | return null; 76 | }); 77 | 78 | 79 | parser.registerFunction("strrev", (arguments) -> { 80 | if(arguments.size() > 0 && arguments.get(0) != null) { 81 | String str = arguments.get(0).toString(); 82 | String reversed = ""; 83 | char ch; 84 | for (int i=0; i { 95 | if(arguments.size() > 1 && arguments.get(0) != null ) { 96 | String str = arguments.get(1) != null ? arguments.get(1).toString() : ""; 97 | Pattern regex = Pattern.compile(arguments.get(0).toString()); 98 | Matcher matcher = regex.matcher(str); 99 | //convert match list to array of results 100 | ArrayList results = new ArrayList<>(); 101 | while(matcher.find()) { 102 | results.add(matcher.group()); 103 | } 104 | return results; 105 | } 106 | return null; 107 | }); 108 | 109 | parser.registerFunction("contains", (arguments) -> { 110 | if(arguments.size() == 2) { 111 | if(arguments.get(0) instanceof Collection) { 112 | Collection list = (Collection)(arguments.get(0)); 113 | try { 114 | return list.contains(arguments.get(1)); 115 | } catch(ClassCastException ex) { 116 | return null; 117 | } 118 | 119 | } else { 120 | //invalid stuff 121 | return null; //Invalid Function Spec 122 | } 123 | } else { 124 | return null; //Invalid Function Spec 125 | } 126 | 127 | }); 128 | 129 | 130 | parser.registerFunction("split", (arguments) -> { 131 | if(arguments.size() == 2) { 132 | String separator = (String)(arguments.get(0)); 133 | try { 134 | String value = (String)(arguments.get(1)); 135 | ArrayList elements = new ArrayList<>(); 136 | if(value == null) { 137 | return null; 138 | } 139 | StringTokenizer tokenizer = new StringTokenizer(value,separator); 140 | while(tokenizer.hasMoreTokens()) { 141 | elements.add(tokenizer.nextToken()); 142 | } 143 | return elements; 144 | } catch(ClassCastException ex) { 145 | return null; 146 | } 147 | 148 | } else { 149 | return null; //Invalid Function Spec 150 | } 151 | }); 152 | 153 | parser.registerFunction("join", (arguments) -> { 154 | if(arguments.size() == 2) { 155 | String separator = (String)(arguments.get(0)); 156 | if(arguments.get(1) instanceof Collection) { 157 | Collection list = (Collection)(arguments.get(1)); 158 | ArrayList elements = new ArrayList<>(); 159 | for(Object listItem : list) { 160 | elements.add(listItem.toString()); 161 | } 162 | return String.join(separator,elements); 163 | } else { 164 | return null; 165 | } 166 | 167 | } else { 168 | return null; //Invalid Function Spec 169 | } 170 | }); 171 | 172 | parser.registerFunction("startswith", (arguments) -> { 173 | if(arguments.size() == 2 && arguments.get(0) != null && arguments.get(1) != null) { 174 | String prefix = (String)(arguments.get(1)); 175 | String value = (String)(arguments.get(0)); 176 | return value.startsWith(prefix); 177 | 178 | } else { 179 | return null; //Invalid Function Spec 180 | } 181 | }); 182 | 183 | parser.registerFunction("endswith", (arguments) -> { 184 | if(arguments.size() == 2) { 185 | String prefix = (String)(arguments.get(1)); 186 | String value = (String)(arguments.get(0)); 187 | return value.endsWith(prefix); 188 | } else { 189 | return null; //Invalid Function Spec 190 | } 191 | }); 192 | 193 | parser.registerFunction("substr", (arguments) -> { 194 | if(arguments.size() == 3 && arguments.get(0) != null) { 195 | Double offset = (Double)(arguments.get(1)); 196 | Double length = (Double)(arguments.get(2)); 197 | String value = (String)(arguments.get(0)); 198 | Integer endIndex = offset.intValue() + length.intValue(); 199 | if(endIndex > value.length() - 1) { 200 | endIndex = value.length(); 201 | } 202 | if(length < 0) { 203 | endIndex = value.length() + 1 + length.intValue(); 204 | } 205 | if(offset < 0) { 206 | offset = value.length() + offset; 207 | } 208 | return value.substring(offset.intValue(),endIndex); 209 | } else { 210 | return null; //Invalid Function Spec 211 | } 212 | }); 213 | 214 | parser.registerFunction("replace", (arguments) -> { 215 | if(arguments.size() == 3) { 216 | String substring = (String)(arguments.get(1)); 217 | String replacement = (String)(arguments.get(2)); 218 | String value = (String)(arguments.get(0)); 219 | if(value == null) { 220 | return null; 221 | } 222 | return value.replace(substring,replacement); 223 | } else { 224 | return null; //Invalid Function Spec 225 | } 226 | }); 227 | 228 | parser.registerFunction("uuid", (arguments) -> { 229 | return java.util.UUID.randomUUID().toString(); 230 | }); 231 | 232 | parser.registerFunction("try", (arguments) -> { 233 | for(Object argument : arguments) { 234 | if(argument != null) { 235 | return argument; 236 | } 237 | } 238 | return null; 239 | }); 240 | 241 | registerNumericFunctions(parser); 242 | registerCollectionFunctions(parser); 243 | registerDateFunctions(parser); 244 | registerCastingFunctions(parser); 245 | } 246 | 247 | static void registerNumericFunctions(HCLParser parser) { 248 | parser.registerFunction("max", (arguments) -> { 249 | Double maxValue = null; 250 | for(Object argument : arguments) { 251 | if(argument instanceof Double) { 252 | if(maxValue == null || (Double)argument > maxValue) { 253 | maxValue = (Double) argument; 254 | } 255 | } 256 | } 257 | return maxValue; 258 | }); 259 | 260 | 261 | parser.registerFunction("tonumber", (arguments) -> { 262 | if(arguments.size() > 0 && arguments.get(0) instanceof String) { 263 | Double val = null; 264 | try { 265 | val = Double.parseDouble((String)(arguments.get(0))); 266 | } catch(NumberFormatException ignore) { 267 | /* ignore */ 268 | } 269 | return val; 270 | } else if(arguments.size() > 0 && arguments.get(0) instanceof Double) { 271 | return arguments.get(0); 272 | } else { 273 | return null; 274 | } 275 | }); 276 | 277 | parser.registerFunction("min", (arguments) -> { 278 | Double minValue = null; 279 | for(Object argument : arguments) { 280 | if(argument instanceof Double) { 281 | if(minValue == null || (Double)argument < minValue) { 282 | minValue = (Double) argument; 283 | } 284 | } 285 | } 286 | return minValue; 287 | }); 288 | 289 | 290 | parser.registerFunction("abs", (arguments) -> { 291 | if(arguments.size() > 0 && arguments.get(0) instanceof Double) { 292 | Double val = (Double)(arguments.get(0)) ; 293 | return Math.abs(val); 294 | 295 | } 296 | return null; 297 | }); 298 | 299 | parser.registerFunction("ceil", (arguments) -> { 300 | if(arguments.size() > 0 && arguments.get(0) instanceof Double) { 301 | Double val = (Double)(arguments.get(0)) ; 302 | return Math.ceil(val); 303 | 304 | } 305 | return null; 306 | }); 307 | 308 | parser.registerFunction("floor", (arguments) -> { 309 | if(arguments.size() > 0 && arguments.get(0) instanceof Double) { 310 | Double val = (Double)(arguments.get(0)) ; 311 | return Math.floor(val); 312 | 313 | } 314 | return null; 315 | }); 316 | 317 | } 318 | 319 | 320 | /** 321 | * Registers the common Terraform Collection Functions as defined in the hashicorp documentation 322 | * Refer to: https://developer.hashicorp.com/terraform/language/functions 323 | * NOTE: This is called from the main "registerBaseFunctions" automatically 324 | * @param parser the parser object we are registering the functions 325 | */ 326 | static void registerCollectionFunctions(HCLParser parser) { 327 | parser.registerFunction("element", (arguments) -> { 328 | if(arguments.size() > 0) { 329 | if(arguments.get(0) instanceof List) { 330 | List elements = ((List)(arguments.get(0))); 331 | Double val = (Double)(arguments.get(1)) ; 332 | return elements.get(val.intValue()); 333 | } else { 334 | return null; 335 | } 336 | } 337 | return null; 338 | }); 339 | 340 | parser.registerFunction("length", (arguments) -> { 341 | if(arguments.size() > 0) { 342 | if(arguments.get(0) instanceof List) { 343 | List elements = ((List)(arguments.get(0))); 344 | return new Double(elements.size()); 345 | } else { 346 | return null; 347 | } 348 | } 349 | return null; 350 | }); 351 | 352 | parser.registerFunction("index", (arguments) -> { 353 | if(arguments.size() > 0) { 354 | if(arguments.get(0) instanceof List) { 355 | List elements = ((List)(arguments.get(0))); 356 | Object val = arguments.get(1); 357 | return elements.indexOf(val); 358 | } else { 359 | return null; 360 | } 361 | } 362 | return null; 363 | }); 364 | 365 | parser.registerFunction("one", (arguments) -> { 366 | if(arguments.size() > 0) { 367 | if(arguments.get(0) instanceof List) { 368 | List elements = ((List)(arguments.get(0))); 369 | if(elements.size() > 0) { 370 | return elements.get(0); 371 | } else { 372 | return null; 373 | } 374 | } else { 375 | return null; 376 | } 377 | } 378 | return null; 379 | }); 380 | 381 | parser.registerFunction("alltrue", (arguments) -> { 382 | if(arguments.size() > 0) { 383 | if(arguments.get(0) instanceof List) { 384 | List elements = ((List)(arguments.get(0))); 385 | if(elements.size() > 0) { 386 | Boolean allTrue = true; 387 | for(Object element : elements) { 388 | if(element instanceof Boolean) { 389 | if(!(Boolean)element) { 390 | allTrue = false; 391 | break; 392 | } 393 | } else if(element instanceof String) { 394 | if(!element.equals("true")) { 395 | allTrue = false; 396 | break; 397 | } 398 | } else { 399 | allTrue = false; 400 | break; 401 | } 402 | } 403 | return allTrue; 404 | } else { 405 | return true; 406 | } 407 | } else { 408 | return true; 409 | } 410 | } 411 | return true; 412 | }); 413 | 414 | 415 | parser.registerFunction("anytrue", (arguments) -> { 416 | if(arguments.size() > 0) { 417 | if(arguments.get(0) instanceof List) { 418 | List elements = ((List)(arguments.get(0))); 419 | if(elements.size() > 0) { 420 | for(Object element : elements) { 421 | if(element instanceof Boolean) { 422 | if((Boolean)element) { 423 | return true; 424 | } 425 | } else if(element instanceof String) { 426 | if(element.equals("true")) { 427 | return true; 428 | } 429 | } 430 | } 431 | } 432 | } 433 | } 434 | return false; 435 | }); 436 | 437 | parser.registerFunction("lookup", (arguments) -> { 438 | if(arguments.size() > 0) { 439 | if(arguments.get(0) instanceof Map) { 440 | Map elements = ((Map)(arguments.get(0))); 441 | String key = ((String)(arguments.get(1))); 442 | Object value = elements.get(key); 443 | if(value == null && arguments.size() > 2) { 444 | value = arguments.get(2); 445 | } 446 | return value; 447 | } else { 448 | return null; 449 | } 450 | } 451 | return null; 452 | }); 453 | 454 | parser.registerFunction("flatten", (arguments) -> { 455 | if(arguments.size() > 0) { 456 | if(arguments.get(0) instanceof List) { 457 | List elements = ((List)(arguments.get(0))); 458 | ArrayList flattened = new ArrayList<>(); 459 | flattenList(flattened,elements); 460 | 461 | return flattened; 462 | } else { 463 | return null; 464 | } 465 | } 466 | return null; 467 | }); 468 | 469 | 470 | } 471 | 472 | private static void flattenList(ArrayList flattened, List elements) { 473 | for(Object element : elements) { 474 | if(element instanceof List) { 475 | List subElements = ((List)(element)); 476 | flattenList(flattened,subElements); 477 | } else { 478 | flattened.add(element); 479 | } 480 | } 481 | 482 | } 483 | 484 | 485 | static void registerDateFunctions(HCLParser parser) { 486 | parser.registerFunction("timestamp", (arguments) -> { 487 | return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX") 488 | .format(new Date()); 489 | }); 490 | } 491 | 492 | static void registerCastingFunctions(HCLParser parser) { 493 | parser.registerFunction("jsonencode", (arguments) -> { 494 | if(arguments.size() > 0) { 495 | ObjectMapper objectMapper = new ObjectMapper(); 496 | try { 497 | return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(arguments.get(0)); 498 | } catch(JsonProcessingException ex) { 499 | //SHOULD WE LOG THIS 500 | } 501 | } 502 | return null; 503 | }); 504 | parser.registerFunction("jsondecode", (arguments) -> { 505 | if(arguments.size() > 0 && arguments.get(0) instanceof String) { 506 | ObjectMapper objectMapper = new ObjectMapper(); 507 | try { 508 | String val = (String)(arguments.get(0)); 509 | JsonNode node = objectMapper.readTree(val); 510 | if(node.getNodeType() == JsonNodeType.OBJECT) { 511 | Map result = objectMapper.convertValue(node, new TypeReference>(){}); 512 | return result; 513 | } else if(node.getNodeType() == JsonNodeType.ARRAY) { 514 | ArrayList result = objectMapper.convertValue(node, new TypeReference>(){}); 515 | return result; 516 | } 517 | } catch(JsonProcessingException ex) { 518 | //SHOULD WE LOG THIS 519 | } 520 | } 521 | return null; 522 | }); 523 | 524 | parser.registerFunction("base64encode", (arguments) -> { 525 | if(arguments.size() > 0) { 526 | String content = (String)(arguments.get(0)); 527 | if(content != null) { 528 | return Base64.getEncoder().encodeToString(content.getBytes(StandardCharsets.UTF_8)); 529 | } 530 | } 531 | return null; 532 | }); 533 | 534 | parser.registerFunction("base64decode", (arguments) -> { 535 | if(arguments.size() > 0) { 536 | String content = (String)(arguments.get(0)); 537 | if(content != null) { 538 | byte[] decodedBytes = Base64.getDecoder().decode(content); 539 | return new String(decodedBytes,StandardCharsets.UTF_8); 540 | } 541 | 542 | } 543 | return null; 544 | }); 545 | 546 | 547 | parser.registerFunction("textencodebase64", (arguments) -> { 548 | if(arguments.size() > 1) { 549 | String content = (String)(arguments.get(0)); 550 | String encoding = (String)(arguments.get(1)); 551 | if(content != null) { 552 | return Base64.getEncoder().encodeToString(content.getBytes(Charset.forName(encoding))); 553 | } 554 | } 555 | return null; 556 | }); 557 | 558 | parser.registerFunction("textdecodebase64", (arguments) -> { 559 | if(arguments.size() > 1) { 560 | String content = (String)(arguments.get(0)); 561 | if(content != null) { 562 | String encoding = (String)(arguments.get(1)); 563 | 564 | byte[] decodedBytes = Base64.getDecoder().decode(content); 565 | return new String(decodedBytes,Charset.forName(encoding)); 566 | } 567 | 568 | } 569 | return null; 570 | }); 571 | } 572 | 573 | 574 | } 575 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/HCLDataLookup.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j; 2 | import java.util.Map; 3 | @FunctionalInterface 4 | public interface HCLDataLookup { 5 | Map method(Map arguments); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/HCLFunction.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j; 2 | 3 | import java.util.List; 4 | 5 | @FunctionalInterface 6 | public interface HCLFunction { 7 | Object method(List arguments); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/HCLParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j; 17 | 18 | import com.bertramlabs.plugins.hcl4j.RuntimeSymbols.*; 19 | import com.bertramlabs.plugins.hcl4j.symbols.*; 20 | 21 | import java.io.*; 22 | import java.nio.charset.Charset; 23 | import java.util.*; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | /** 28 | * Parser for the Hashicorp Configuration Language (HCL). This is the primary endpoint and converts the HCL syntax into a {@link Map}. 29 | * This parser utilizes a lexer to generate symbols based on the HCL spec. String interpolation is not evaluated at this point in the parsing. 30 | * 31 | *

32 | * Below is an example of how HCL might be parsed. 33 | *

34 | *
  35 |  *     {@code
  36 |  *     import com.bertramlabs.plugins.hcl4j.HCLParser;
  37 |  *
  38 |  *     File terraformFile = new File("terraform.tf");
  39 |  *
  40 |  *     Map results = new HCLParser().parse(terraformFile);
  41 |  *     }
  42 |  * 
43 | * @author David Estes 44 | */ 45 | public class HCLParser { 46 | static Logger log = LoggerFactory.getLogger(HCLParser.class); 47 | //Time to parse the AST Tree into a Map 48 | protected Map result = new LinkedHashMap<>(); 49 | protected Map variables = new LinkedHashMap<>(); 50 | protected Map dataLookups = new LinkedHashMap<>(); 51 | protected Map functionRegistry = new LinkedHashMap<>(); 52 | protected Map dataLookupRegistry = new LinkedHashMap<>(); 53 | 54 | public HCLParser() { 55 | HCLBaseFunctions.registerBaseFunctions(this); 56 | HCLBaseDataLookups.registerBaseFunctions(this); 57 | } 58 | 59 | /** 60 | * Parses Var files into the variables context (example would be a tfvars file from terraform) 61 | * @param input String input containing HCL syntax 62 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 63 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 64 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 65 | */ 66 | public Map parseVars(String input, Boolean ignoreParseException) throws HCLParserException, IOException { 67 | HCLParser varLoader = new HCLParser(); 68 | Map results = varLoader.parse(input,ignoreParseException); 69 | for(String key : results.keySet()) { 70 | variables.put(key,results.get(key)); 71 | } 72 | return variables; 73 | } 74 | 75 | /** 76 | * Registers Function implementations for HCL Common functions. By default the {@link HCLBaseFunctions} are loaded. 77 | * Additional functions can be defined via this method for custom method overrides if necessary. 78 | * @param functionName the name of the function to be called 79 | * @param function the lambda function implementation to be evaluated during the parse 80 | */ 81 | public void registerFunction(String functionName, HCLFunction function) { 82 | this.functionRegistry.put(functionName,function); 83 | } 84 | 85 | /** 86 | * Registers Data Lookup implementations for HCL references. By default, the {@link HCLDataLookup} are loaded. 87 | * Additional data lookups can be defined via this method for custom lookup providers if necessary. 88 | * @param lookupName the name of the data lookup to be called 89 | * @param dataLookup the lambda function implementation to be evaluated during the parse 90 | */ 91 | public void registerDataLookup(String lookupName, HCLDataLookup dataLookup) { 92 | this.dataLookupRegistry.put(lookupName,dataLookup); 93 | } 94 | 95 | /** 96 | * Sets a variable value individually. One can also use the parseTfVars method to load tf vars. 97 | * @param variableName the name of the variable being defined 98 | * @param value the value of the variable 99 | */ 100 | public void setVariable(String variableName, Object value) { 101 | this.variables.put(variableName,value); 102 | } 103 | 104 | /** 105 | * Sets a Map of variables into the context of the HCLParser for parse runtime operations 106 | * @param variableMap A Map of variables to be bulk applied to the parser Context 107 | */ 108 | public void setVariables(Map variableMap) { 109 | for(String key : variableMap.keySet()) { 110 | variables.put(key,variableMap.get(key)); 111 | } 112 | } 113 | 114 | 115 | /** 116 | * Parses terraform configuration language from a String 117 | * @param input String input containing HCL syntax 118 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 119 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 120 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 121 | */ 122 | public Map parse(String input) throws HCLParserException, IOException { 123 | return parse(input,false); 124 | } 125 | 126 | /** 127 | * Parses terraform configuration language from a String 128 | * @param input String input containing HCL syntax 129 | * @param ignoreParserExceptions if set to true, we ignore any parse exceptions and still return the symbol map 130 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 131 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 132 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 133 | */ 134 | public Map parse(String input, Boolean ignoreParserExceptions) throws HCLParserException, IOException { 135 | StringReader reader = new StringReader(input); 136 | return parse(reader,ignoreParserExceptions); 137 | } 138 | 139 | 140 | /** 141 | * Parses terraform syntax as it comes from a File. 142 | * @param input A source file to process with a default charset of UTF-8 143 | * @param ignoreParserExceptions if set to true, we ignore any parse exceptions and still return the symbol map 144 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 145 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 146 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 147 | */ 148 | public Map parse(File input, Boolean ignoreParserExceptions) throws HCLParserException, IOException, UnsupportedEncodingException { 149 | return parse(input,"UTF-8",ignoreParserExceptions); 150 | } 151 | 152 | /** 153 | * Parses terraform syntax as it comes from a File. 154 | * @param input A source file to process with a default charset of UTF-8 155 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 156 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 157 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 158 | */ 159 | public Map parse(File input) throws HCLParserException, IOException, UnsupportedEncodingException { 160 | return parse(input,"UTF-8",false); 161 | } 162 | 163 | 164 | /** 165 | * Parses terraform syntax as it comes from a File. 166 | * closed at the end of the parse operation (commonly via wrapping in a finally block) 167 | * @param input A source file to process 168 | * @param cs A charset 169 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 170 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 171 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 172 | */ 173 | public Map parse(File input, Charset cs) throws HCLParserException, IOException{ 174 | InputStream is = null; 175 | try { 176 | is = new FileInputStream(input); 177 | return parse(is,cs); 178 | } finally { 179 | if(is != null) { 180 | is.close(); 181 | } 182 | } 183 | } 184 | 185 | 186 | /** 187 | * Parses terraform syntax as it comes from a File. 188 | * @param input A source file to process 189 | * @param charsetName The name of a supported charset 190 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 191 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 192 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 193 | * @throws UnsupportedEncodingException If the charset ( UTF-8 by default if unspecified) encoding is not supported 194 | */ 195 | public Map parse(File input, String charsetName) throws HCLParserException, IOException { 196 | return parse(input,charsetName,false); 197 | } 198 | 199 | /** 200 | * Parses terraform syntax as it comes from a File. 201 | * @param input A source file to process 202 | * @param charsetName The name of a supported charset 203 | * @param ignoreParserExceptions if set to true, we ignore any parse exceptions and still return the symbol map 204 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 205 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 206 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 207 | * @throws UnsupportedEncodingException If the charset ( UTF-8 by default if unspecified) encoding is not supported 208 | */ 209 | public Map parse(File input, String charsetName, Boolean ignoreParserExceptions) throws HCLParserException, IOException { 210 | InputStream is = null; 211 | try { 212 | is = new FileInputStream(input); 213 | return parse(is,charsetName,ignoreParserExceptions); 214 | } finally { 215 | if(is != null) { 216 | is.close(); 217 | } 218 | } 219 | } 220 | 221 | 222 | 223 | /** 224 | * Parses terraform syntax as it comes from an input stream. The end user is responsible for ensuring the stream is 225 | * closed at the end of the parse operation (commonly via wrapping in a finally block) 226 | * @param input Streamable input of text going to the lexer 227 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 228 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 229 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 230 | */ 231 | public Map parse(InputStream input) throws HCLParserException, IOException { 232 | return parse(input,"UTF-8"); 233 | } 234 | 235 | /** 236 | * Parses terraform syntax as it comes from an input stream. The end user is responsible for ensuring the stream is 237 | * closed at the end of the parse operation (commonly via wrapping in a finally block) 238 | * @param input Streamable input of text going to the lexer 239 | * @param cs CharSet with which to read the contents of the stream (default UTF-8) 240 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 241 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 242 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 243 | */ 244 | public Map parse(InputStream input, Charset cs) throws HCLParserException, IOException { 245 | 246 | InputStreamReader reader; 247 | if(cs != null) { 248 | reader = new InputStreamReader(input,cs); 249 | } else { 250 | reader = new InputStreamReader(input,"UTF-8"); 251 | } 252 | return parse(reader); 253 | } 254 | 255 | 256 | /** 257 | * Parses terraform syntax as it comes from an input stream. The end user is responsible for ensuring the stream is 258 | * closed at the end of the parse operation (commonly via wrapping in a finally block) 259 | * @param input Streamable input of text going to the lexer 260 | * @param charsetName String lookup of the character set this stream is providing (default UTF-8) 261 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 262 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 263 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 264 | * @throws UnsupportedEncodingException If the charset ( UTF-8 by default if unspecified) encoding is not supported. 265 | */ 266 | public Map parse(InputStream input, String charsetName) throws HCLParserException, IOException, UnsupportedEncodingException { 267 | return parse(input,charsetName,false); 268 | } 269 | 270 | /** 271 | * Parses terraform syntax as it comes from an input stream. The end user is responsible for ensuring the stream is 272 | * closed at the end of the parse operation (commonly via wrapping in a finally block) 273 | * @param input Streamable input of text going to the lexer 274 | * @param charsetName String lookup of the character set this stream is providing (default UTF-8) 275 | * @param ignoreParserExceptions if set to true, we ignore any parse exceptions and still return the symbol map 276 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 277 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 278 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 279 | * @throws UnsupportedEncodingException If the charset ( UTF-8 by default if unspecified) encoding is not supported. 280 | */ 281 | public Map parse(InputStream input, String charsetName, Boolean ignoreParserExceptions) throws HCLParserException, IOException, UnsupportedEncodingException { 282 | 283 | InputStreamReader reader; 284 | if(charsetName != null) { 285 | reader = new InputStreamReader(input,charsetName); 286 | } else { 287 | reader = new InputStreamReader(input,"UTF-8"); 288 | } 289 | return parse(reader,ignoreParserExceptions); 290 | } 291 | 292 | /** 293 | * Parses terraform configuration language from a Reader 294 | * @param reader A reader object used for absorbing various streams or String variables containing the hcl code 295 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 296 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 297 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 298 | */ 299 | public Map parse(Reader reader) throws HCLParserException, IOException { 300 | return parse(reader,false); 301 | } 302 | 303 | /** 304 | * Parses terraform configuration language from a Reader 305 | * @param reader A reader object used for absorbing various streams or String variables containing the hcl code 306 | * @param ignoreParserExceptions if set to true, we ignore any parse exceptions and still return the symbol map 307 | * @return Mapped result of object tree coming from HCL (values of keys can be variable). 308 | * @throws HCLParserException Any type of parsing errors are returned as this exception if the syntax is invalid. 309 | * @throws IOException In the event the reader is unable to pull from the input source this exception is thrown. 310 | */ 311 | public Map parse(Reader reader, Boolean ignoreParserExceptions) throws HCLParserException, IOException { 312 | HCLLexer lexer = new HCLLexer(reader); 313 | ArrayList rootBlocks; 314 | dataLookups = new LinkedHashMap<>(); 315 | try { 316 | lexer.yylex(); 317 | rootBlocks = lexer.elementStack; 318 | 319 | result = new LinkedHashMap<>(); 320 | Map mapPosition = result; 321 | 322 | for (Symbol currentElement : rootBlocks) { 323 | intermediateHclParserExceptionHandling(() -> processSymbolPass1(currentElement, mapPosition), ignoreParserExceptions); 324 | } 325 | 326 | //pass2 327 | if (result.get("locals") != null) { 328 | intermediateHclParserExceptionHandling(() -> processSymbolPass2(result.get("locals"), result), ignoreParserExceptions); 329 | } 330 | if (result.get("variable") != null) { 331 | intermediateHclParserExceptionHandling(() -> processSymbolPass2(result.get("variable"), result), ignoreParserExceptions); 332 | } 333 | if (result.get("data") != null) { 334 | 335 | intermediateHclParserExceptionHandling(() -> processSymbolPass2(result.get("data"), result), ignoreParserExceptions); 336 | 337 | } 338 | for (String key : result.keySet().toArray(new String[0])) { 339 | intermediateHclParserExceptionHandling(() -> { 340 | if (!Objects.equals(key, "variable") && !Objects.equals(key, "data") && !Objects.equals(key, "locals")) { 341 | processSymbolPass2(result.get(key), result); 342 | } 343 | }, ignoreParserExceptions); 344 | } 345 | } catch (Exception ex) { 346 | log.error("Error Parsing HCL...{}", ex.getMessage(), ex); 347 | if (ignoreParserExceptions != true) { //its nullable so thats why we look at inverse 348 | throw new RuntimeException(ex); 349 | } 350 | } 351 | return result; 352 | } 353 | 354 | 355 | 356 | @FunctionalInterface 357 | interface ExceptionThrowingRunnable { 358 | void run() throws Exception; 359 | } 360 | 361 | void intermediateHclParserExceptionHandling(ExceptionThrowingRunnable runnable, Boolean ignore) throws Exception { 362 | try { 363 | runnable.run(); 364 | } catch (Exception ex) { 365 | log.error("Error Parsing HCL...{}", ex.getMessage(), ex); 366 | if (ignore != true) { //its nullable so thats why we look at inverse 367 | throw ex; 368 | } 369 | } 370 | } 371 | 372 | 373 | private Object processSymbolPass1(Symbol symbol, Map mapPosition) throws HCLParserException { 374 | 375 | if(symbol instanceof HCLBlock) { 376 | HCLBlock block = (HCLBlock)symbol; 377 | for(int counter = 0 ; counter < block.blockNames.size() ; counter++) { 378 | String blockName = block.blockNames.get(counter); 379 | if(mapPosition.containsKey(blockName)) { 380 | if(counter == block.blockNames.size() - 1 && mapPosition.get(blockName) instanceof Map) { 381 | List> objectList = new ArrayList<>(); 382 | Map addedObject = new LinkedHashMap(); 383 | objectList.add((Map)mapPosition.get(blockName)); 384 | objectList.add(addedObject); 385 | mapPosition.put(blockName,objectList); 386 | mapPosition = addedObject; 387 | } else if(mapPosition.get(blockName) instanceof Map) { 388 | mapPosition = (Map) mapPosition.get(blockName); 389 | } else if(counter == block.blockNames.size() - 1 && mapPosition.get(blockName) instanceof List) { 390 | Map addedObject = new LinkedHashMap(); 391 | ((List)mapPosition.get(blockName)).add(addedObject); 392 | mapPosition = addedObject; 393 | } else { 394 | if(mapPosition.get(blockName) instanceof List) { 395 | throw new HCLParserException("HCL Block expression scope traverses an object array"); 396 | } else { 397 | throw new HCLParserException("HCL Block expression scope traverses an object value"); 398 | } 399 | } 400 | } else { 401 | mapPosition.put(blockName,new LinkedHashMap()); 402 | mapPosition = (Map) mapPosition.get(blockName); 403 | } 404 | } 405 | if(symbol.getChildren() != null) { 406 | for(Symbol child : block.getChildren()) { 407 | processSymbolPass1(child,mapPosition); 408 | } 409 | } 410 | return mapPosition; 411 | } else if(symbol instanceof HCLMap) { 412 | Map nestedMap = new LinkedHashMap<>(); 413 | if(symbol.getChildren() != null) { 414 | for(Symbol child : symbol.getChildren()) { 415 | processSymbolPass1(child, nestedMap); 416 | } 417 | } 418 | return nestedMap; 419 | } else if(symbol instanceof HCLArray) { 420 | if(symbol.getChildren() != null) { 421 | List objectList = new ArrayList<>(); 422 | for(Symbol child : symbol.getChildren()) { 423 | Map nestedMap = new LinkedHashMap<>(); 424 | Object result = processSymbolPass1(child,nestedMap); 425 | objectList.add(result); 426 | } 427 | return objectList; 428 | } else { 429 | return null; 430 | } 431 | } else if(symbol instanceof HCLValue) { 432 | return processValue((HCLValue) symbol); 433 | } else if(symbol instanceof PrimitiveType) { 434 | return symbol; 435 | } else if(symbol instanceof EvalSymbol) { 436 | return processEvaluation((EvalSymbol) symbol,null); 437 | } else if(symbol instanceof HCLAttribute) { 438 | String symName = symbol.getName(); 439 | if(symName == null && ((HCLAttribute) symbol).runtimeName != null) { 440 | Map nestedMap = new LinkedHashMap<>(); 441 | symName = (String)(processSymbolPass2(((HCLAttribute) symbol).runtimeName,nestedMap)); 442 | } 443 | 444 | if(symbol.getChildren().size() == 1 && symbol.getChildren().get(0) instanceof HCLBlock) { 445 | Object results = null; 446 | Map nestedMap = new LinkedHashMap<>(); 447 | results = processSymbolPass1(symbol.getChildren().get(0),nestedMap); 448 | mapPosition.put(symName,results); 449 | } else { 450 | mapPosition.put(symName,symbol); 451 | } 452 | 453 | return mapPosition; 454 | } 455 | return null; 456 | } 457 | 458 | private Object processSymbolPass2(Object val, Map mapPosition) throws HCLParserException { 459 | 460 | 461 | if(val instanceof Map) { 462 | Map subMap = (Map) (val); 463 | for (String key : subMap.keySet()) { 464 | processSymbolPass2(subMap.get(key), subMap); 465 | } 466 | } else if(val instanceof ArrayList) { 467 | ArrayList currentCollection = (ArrayList)(val); 468 | for(int x=0;x nestedMap = new LinkedHashMap<>(); 478 | if((symbol.getChildren() != null)) { 479 | for(Symbol child : symbol.getChildren()) { 480 | processSymbolPass2(child, nestedMap); 481 | } 482 | } 483 | return nestedMap; 484 | } else if(val instanceof HCLBlock) { 485 | HCLBlock symbol = (HCLBlock)(val); 486 | Map nestedMap = new LinkedHashMap<>(); 487 | if((symbol.getChildren() != null)) { 488 | for(Symbol child : symbol.getChildren()) { 489 | processSymbolPass2(child, nestedMap); 490 | } 491 | } 492 | return nestedMap; 493 | } else if(val instanceof HCLArray) { 494 | HCLArray symbol = (HCLArray)(val); 495 | if(symbol.getChildren() != null) { 496 | 497 | List objectList = new ArrayList<>(); 498 | for(Symbol child : symbol.getChildren()) { 499 | Map nestedMap = new LinkedHashMap<>(); 500 | Object result = processSymbolPass2(child,nestedMap); 501 | objectList.add(result); 502 | } 503 | return objectList; 504 | } else { 505 | return null; 506 | } 507 | } else if(val instanceof HCLValue) { 508 | return processValue((HCLValue) val); 509 | } else if(val instanceof PrimitiveType) { 510 | return val; 511 | } else if(val instanceof EvalSymbol) { 512 | return processEvaluation((EvalSymbol) val,null); 513 | } else if(val instanceof HCLAttribute || val instanceof GroupedExpression || val instanceof StringInterpolatedExpression) { 514 | 515 | Symbol symbol = (Symbol) val; 516 | 517 | Map nestedMap = new LinkedHashMap<>(); 518 | if(symbol.getChildren().size() > 0) { 519 | Object results = null; 520 | for(int x = 0;x": 579 | if(results instanceof Double) { 580 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 581 | if(rightResult != null && rightResult instanceof Double) { 582 | results = ((Double)results > (Double)rightResult); 583 | } else { 584 | //TODO: Exception? 585 | } 586 | } 587 | break; 588 | case ">=": 589 | if(results instanceof Double) { 590 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 591 | if(rightResult != null && rightResult instanceof Double) { 592 | results = ((Double)results >= (Double)rightResult); 593 | } else { 594 | //TODO: Exception? 595 | } 596 | } 597 | break; 598 | case "<": 599 | if(results instanceof Double) { 600 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 601 | if(rightResult != null && rightResult instanceof Double) { 602 | results = ((Double)results < (Double)rightResult); 603 | } else { 604 | //TODO: Exception? 605 | } 606 | } 607 | break; 608 | case "<=": 609 | if(results instanceof Double) { 610 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 611 | if(rightResult != null && rightResult instanceof Double) { 612 | results = ((Double)results <= (Double)rightResult); 613 | } else { 614 | //TODO: Exception? 615 | } 616 | } 617 | break; 618 | case "!=": 619 | Object compareResult2 = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 620 | if(compareResult2 != results && (compareResult2 == null || !compareResult2.equals(results))) { 621 | results = true; 622 | } else { 623 | results = false; 624 | } 625 | break; 626 | case "!": 627 | Object notResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 628 | if(notResult == null || notResult.equals(false)) { 629 | results = true; 630 | } else { 631 | results = false; 632 | } 633 | break; 634 | case "+": 635 | if(results instanceof String) { 636 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 637 | if(rightResult != null) { 638 | results = (String)results + rightResult.toString(); 639 | } else { 640 | //TODO: Exception? 641 | } 642 | } else if(results instanceof Double) { 643 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 644 | if(rightResult != null && rightResult instanceof Double) { 645 | results = (Double)results + (Double)rightResult; 646 | } else { 647 | //TODO: Exception? 648 | } 649 | } else if(results == null) { 650 | results = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 651 | } 652 | break; 653 | case "-": 654 | if(results instanceof Double) { 655 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 656 | if(rightResult != null && rightResult instanceof Double) { 657 | results = (Double)results - (Double)rightResult; 658 | } else { 659 | //TODO: Exception? 660 | } 661 | } else if(results == null) { 662 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 663 | if(rightResult != null && rightResult instanceof Double) { 664 | results = - (Double)rightResult; 665 | } else { 666 | //TODO: Exception? 667 | } 668 | } 669 | break; 670 | case "/": 671 | if(results instanceof Double) { 672 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 673 | if(rightResult != null && rightResult instanceof Double) { 674 | results = (Double)results / (Double)rightResult; 675 | } else { 676 | //TODO: Exception? 677 | } 678 | } 679 | break; 680 | case "%": 681 | if(results instanceof Double) { 682 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 683 | if(rightResult != null && rightResult instanceof Double) { 684 | results = (Double)results % (Double)rightResult; 685 | } else { 686 | //TODO: Exception? 687 | } 688 | } 689 | break; 690 | case "*": 691 | if(results instanceof Double) { 692 | Object rightResult = processSymbolPass2(symbol.getChildren().get(++x),nestedMap); 693 | if(rightResult != null && rightResult instanceof Double) { 694 | results = (Double)results * (Double)rightResult; 695 | } else { 696 | //TODO: Exception? 697 | } 698 | } 699 | break; 700 | } 701 | } else { 702 | results = processSymbolPass2(child,nestedMap); 703 | } 704 | } 705 | 706 | if(symbol instanceof GroupedExpression || symbol instanceof StringInterpolatedExpression) { 707 | return results; 708 | } else { 709 | mapPosition.put(symbol.getName(),results); 710 | } 711 | 712 | } else { 713 | if(symbol instanceof GroupedExpression || symbol instanceof StringInterpolatedExpression) { 714 | return null; 715 | } else { 716 | mapPosition.put(symbol.getName(),null); 717 | } 718 | 719 | } 720 | return mapPosition; 721 | } 722 | return null; 723 | } 724 | 725 | private Object processSymbol(Symbol symbol, Map mapPosition) throws HCLParserException { 726 | return processSymbol(symbol,mapPosition,null); 727 | } 728 | private Object processSymbol(Symbol symbol, Map mapPosition, Map stackVars) throws HCLParserException { 729 | 730 | if(symbol instanceof HCLBlock) { 731 | HCLBlock block = (HCLBlock)symbol; 732 | for(int counter = 0 ; counter < block.blockNames.size() ; counter++) { 733 | String blockName = block.blockNames.get(counter); 734 | if(mapPosition.containsKey(blockName)) { 735 | if(counter == block.blockNames.size() - 1 && mapPosition.get(blockName) instanceof Map) { 736 | List> objectList = new ArrayList<>(); 737 | Map addedObject = new LinkedHashMap(); 738 | objectList.add((Map)mapPosition.get(blockName)); 739 | objectList.add(addedObject); 740 | mapPosition.put(blockName,objectList); 741 | mapPosition = addedObject; 742 | } else if(mapPosition.get(blockName) instanceof Map) { 743 | mapPosition = (Map) mapPosition.get(blockName); 744 | } else if(counter == block.blockNames.size() - 1 && mapPosition.get(blockName) instanceof List) { 745 | Map addedObject = new LinkedHashMap(); 746 | ((List)mapPosition.get(blockName)).add(addedObject); 747 | mapPosition = addedObject; 748 | } else { 749 | if(mapPosition.get(blockName) instanceof List) { 750 | throw new HCLParserException("HCL Block expression scope traverses an object array"); 751 | } else { 752 | throw new HCLParserException("HCL Block expression scope traverses an object value"); 753 | } 754 | } 755 | } else { 756 | mapPosition.put(blockName,new LinkedHashMap()); 757 | mapPosition = (Map) mapPosition.get(blockName); 758 | } 759 | } 760 | if(symbol.getChildren() != null) { 761 | for(Symbol child : block.getChildren()) { 762 | processSymbol(child,mapPosition,stackVars); 763 | } 764 | } 765 | return mapPosition; 766 | } else if(symbol instanceof HCLMap) { 767 | Map nestedMap = new LinkedHashMap<>(); 768 | if(symbol.getChildren() != null) { 769 | for(Symbol child : symbol.getChildren()) { 770 | processSymbol(child, nestedMap); 771 | } 772 | } 773 | return nestedMap; 774 | } else if(symbol instanceof HCLArray) { 775 | if(symbol.getChildren() != null) { 776 | List objectList = new ArrayList<>(); 777 | for(Symbol child : symbol.getChildren()) { 778 | Map nestedMap = new LinkedHashMap<>(); 779 | Object result = processSymbol(child,nestedMap); 780 | objectList.add(result); 781 | } 782 | return objectList; 783 | } else { 784 | return null; 785 | } 786 | } else if(symbol instanceof HCLValue) { 787 | return processValue((HCLValue) symbol); 788 | } else if(symbol instanceof PrimitiveType) { 789 | return symbol; 790 | } else if(symbol instanceof EvalSymbol) { 791 | return processEvaluation((EvalSymbol) symbol,null,stackVars); 792 | } else if(symbol instanceof HCLAttribute || symbol instanceof GroupedExpression || symbol instanceof StringInterpolatedExpression) { 793 | Map nestedMap = new LinkedHashMap<>(); 794 | if(symbol.getChildren().size() > 0) { 795 | Object results = null; 796 | for(int x = 0;x": 879 | if(results instanceof Double) { 880 | Object rightResult = processSymbol(symbol.getChildren().get(++x),nestedMap,stackVars); 881 | if(rightResult != null && rightResult instanceof Double) { 882 | results = ((Double)results > (Double)rightResult); 883 | } else { 884 | //TODO: Exception? 885 | } 886 | } 887 | break; 888 | case ">=": 889 | if(results instanceof Double) { 890 | Object rightResult = processSymbol(symbol.getChildren().get(++x),nestedMap,stackVars); 891 | if(rightResult != null && rightResult instanceof Double) { 892 | results = ((Double)results >= (Double)rightResult); 893 | } else { 894 | //TODO: Exception? 895 | } 896 | } 897 | break; 898 | case "<": 899 | if(results instanceof Double) { 900 | Object rightResult = processSymbol(symbol.getChildren().get(++x),nestedMap,stackVars); 901 | if(rightResult != null && rightResult instanceof Double) { 902 | results = ((Double)results < (Double)rightResult); 903 | } else { 904 | //TODO: Exception? 905 | } 906 | } 907 | break; 908 | case "<=": 909 | if(results instanceof Double) { 910 | Object rightResult = processSymbol(symbol.getChildren().get(++x),nestedMap,stackVars); 911 | if(rightResult != null && rightResult instanceof Double) { 912 | results = ((Double)results <= (Double)rightResult); 913 | } else { 914 | //TODO: Exception? 915 | } 916 | } 917 | break; 918 | } 919 | } else { 920 | results = processSymbol(symbol.getChildren().get(0),nestedMap,stackVars); 921 | } 922 | } 923 | 924 | if(symbol instanceof GroupedExpression || symbol instanceof StringInterpolatedExpression) { 925 | return results; 926 | } else { 927 | mapPosition.put(symbol.getName(),results); 928 | } 929 | 930 | } else { 931 | if(symbol instanceof GroupedExpression) { 932 | return null; 933 | } else { 934 | mapPosition.put(symbol.getName(),null); 935 | } 936 | 937 | } 938 | return mapPosition; 939 | } 940 | return null; 941 | } 942 | 943 | 944 | protected Object processValue(HCLValue value) throws HCLParserException { 945 | if(value.type.equals("string")) { 946 | return value.value; 947 | } else if (value.type.equals("boolean")) { 948 | if(value.value.equals("true")) { 949 | return new Boolean(true); 950 | } else { 951 | return new Boolean(false); 952 | } 953 | } else if (value.type.equals("null")) { 954 | return null; 955 | } else if (value.type.equals("number")) { 956 | try { 957 | Double numericalValue = Double.parseDouble((String) (value.value)); 958 | return numericalValue; 959 | } catch(NumberFormatException ex) { 960 | throw new HCLParserException("Error Parsing Numerical Value in HCL Attribute ", ex); 961 | } 962 | } else { 963 | throw new HCLParserException("HCL Attribute value not recognized by parser (not implemented yet)."); 964 | } 965 | } 966 | 967 | protected Object evaluateFunctionCall(String functionName,Function functionSymbol) throws HCLParserException { 968 | return evaluateFunctionCall(functionName,functionSymbol,null); 969 | } 970 | protected Object evaluateFunctionCall(String functionName,Function functionSymbol, Map localVars) throws HCLParserException { 971 | if(functionRegistry.get(functionName) != null) { 972 | HCLFunction functionMethod = functionRegistry.get(functionName); 973 | ArrayList functionArguments = new ArrayList<>(); 974 | for(Symbol child : functionSymbol.getChildren()) { 975 | Object elementResult = null; 976 | if(child instanceof EvalSymbol) { 977 | elementResult = processEvaluation((EvalSymbol) child,null,localVars); 978 | } else if(child instanceof Symbol) { 979 | elementResult = processSymbol(child,result,localVars); 980 | } 981 | if(elementResult instanceof VariableTree) { //we were unable to evaluate a sub argument... abort function evaluation 982 | return null; // function evaluation aborted. perhaps throw error for later based on if ignoreExceptions is on or not 983 | } 984 | functionArguments.add(elementResult); 985 | } 986 | return functionMethod.method(functionArguments); 987 | } else { 988 | //TODO: DO we throw a method missing exception at some point 989 | return null; 990 | } 991 | } 992 | 993 | 994 | protected Object evaluateComputedTuple(ComputedTuple thisTuple) throws HCLParserException { 995 | return evaluateComputedTuple(thisTuple,null); 996 | } 997 | protected Object evaluateComputedTuple(ComputedTuple thisTuple, Map localVars) throws HCLParserException { 998 | 999 | ArrayList computedResult = new ArrayList(); 1000 | Variable indexVariable = null; 1001 | Variable valueVariable = null; 1002 | if(thisTuple.getVariables().size() == 2) { 1003 | //variables with index 1004 | 1005 | indexVariable = thisTuple.getVariables().get(0); 1006 | valueVariable = thisTuple.getVariables().get(1); 1007 | } else if(thisTuple.getVariables().size() == 1) { 1008 | valueVariable = thisTuple.getVariables().get(0); 1009 | } else { 1010 | log.warn("Computed Tuple loop found with too many variables at line {}",thisTuple.getLine()); 1011 | return null; 1012 | } 1013 | System.out.println("Evaluating Tuple: " + valueVariable.getName()); 1014 | ForConditional tupleConditional = null; 1015 | for(Symbol child : thisTuple.getChildren()) { 1016 | if(child instanceof ForConditional) { 1017 | tupleConditional = (ForConditional) child; 1018 | thisTuple.getChildren().remove(child); 1019 | break; 1020 | } 1021 | } 1022 | Object elementResult = null; 1023 | Boolean forSourceFound=false; 1024 | GroupedExpression sourceExpression = new GroupedExpression(0,0,0L); 1025 | GroupedExpression iteratorExpression = new GroupedExpression(0,0,0L); 1026 | for(Symbol child : thisTuple.getChildren()) { 1027 | if(child instanceof ForSource) { 1028 | forSourceFound = true; 1029 | continue; 1030 | } 1031 | if(forSourceFound) { 1032 | iteratorExpression.appendChild(child); 1033 | } else { 1034 | sourceExpression.appendChild(child); 1035 | } 1036 | 1037 | } 1038 | if(sourceExpression.getChildren().size() > 0 && iteratorExpression.getChildren().size() > 0) { 1039 | elementResult = processSymbol(sourceExpression,result,localVars); 1040 | if(elementResult instanceof List) { 1041 | List elementResultList = (List)elementResult; 1042 | System.out.println("Iterating over list"); 1043 | for(int counter=0;counter < elementResultList.size();counter++){ 1044 | 1045 | Map stackVars = new LinkedHashMap<>(); 1046 | if(localVars != null) { 1047 | stackVars = new LinkedHashMap<>(localVars); 1048 | } 1049 | if(indexVariable != null) { 1050 | stackVars.put(indexVariable.getName(),counter); 1051 | } 1052 | stackVars.put(valueVariable.getName(),elementResultList.get(counter)); 1053 | if(tupleConditional != null) { 1054 | System.out.println("Conditional Tuple Found"); 1055 | Object conditionalResult = processSymbol(tupleConditional ,null,stackVars); 1056 | System.out.println("Conditional Result: " + conditionalResult); 1057 | if(conditionalResult == null || ((conditionalResult instanceof Boolean) && (Boolean) conditionalResult == false)) { 1058 | System.out.println("Does not match"); 1059 | continue; 1060 | } else { 1061 | System.out.println("Matches"); 1062 | } 1063 | } 1064 | computedResult.add(processSymbol(iteratorExpression,null,stackVars)); 1065 | System.out.println("Computed Result: " + computedResult); 1066 | } 1067 | } else { 1068 | log.error("Error Processing Tuple from source not a List"); 1069 | return computedResult; 1070 | } 1071 | } 1072 | return computedResult; 1073 | } 1074 | 1075 | protected Map evaluateDataLookup(String lookupName,String name,Map properties) throws HCLParserException { 1076 | if(dataLookupRegistry.get(lookupName) != null) { 1077 | try { 1078 | HCLDataLookup lookupMethod = dataLookupRegistry.get(lookupName); 1079 | Map mapPosition = (Map)(((Map)(result.get("data"))).get(lookupName)); 1080 | processSymbolPass2(mapPosition.get(name),mapPosition); 1081 | Map lookupResults = lookupMethod.method(properties); 1082 | 1083 | Map dataTypeLookup = (Map)(dataLookups.get(lookupName)); 1084 | if(dataTypeLookup == null) { 1085 | dataTypeLookup = new LinkedHashMap<>(); 1086 | dataLookups.put(lookupName,dataTypeLookup); 1087 | } 1088 | dataTypeLookup.put(name,lookupResults); 1089 | return lookupResults; 1090 | } catch(Exception ex) { 1091 | log.warn("Error Occurred Performing Data Source Lookup on {} from provider {}: {}",name,lookupName,ex.getMessage(),ex); 1092 | } 1093 | return null; 1094 | } else { 1095 | //TODO: DO we throw a method missing exception at some point 1096 | return null; 1097 | } 1098 | 1099 | 1100 | } 1101 | 1102 | protected Object processEvaluation(EvalSymbol evalSymbol, Object context) throws HCLParserException{ 1103 | return processEvaluation(evalSymbol,context,null); 1104 | } 1105 | protected Object processEvaluation(EvalSymbol evalSymbol, Object context,Map stackVars) throws HCLParserException{ 1106 | Boolean variableLookup = false; 1107 | Boolean dataLookup = false; 1108 | String dataLookupName = null; 1109 | 1110 | if(evalSymbol instanceof VariableTree) { 1111 | for(int x = 0;x x+1 && evalSymbol.getChildren().get(x+1) instanceof Function) { 1115 | //This may not be necessary anymore but is there to catch a function traversal potential error 1116 | return evaluateFunctionCall(child.getName(), (Function) (evalSymbol.getChildren().get(x + 1))); 1117 | } else if(child instanceof Function) { 1118 | context = evaluateFunctionCall(child.getName(), (Function) child,stackVars); 1119 | if(context == null) { //function call failed 1120 | return null; 1121 | } 1122 | }else if(child instanceof Variable) { 1123 | if(context == null && x == 0) { 1124 | switch(child.getName()) { 1125 | case "local": 1126 | context = flattenContext(result.get("locals")); 1127 | break; 1128 | case "var": 1129 | variableLookup = true; 1130 | context = variables; 1131 | break; 1132 | case "data": 1133 | log.info("Data Lookup Detected!"); 1134 | dataLookup = true; 1135 | context = dataLookups; 1136 | break; 1137 | default: 1138 | if(stackVars != null) { 1139 | context = stackVars.get(child.getName()); 1140 | if(context == null) { 1141 | context = result.get(child.getName()); 1142 | } 1143 | } else { 1144 | context = result.get(child.getName()); 1145 | } 1146 | } 1147 | 1148 | } else if(context != null){ 1149 | if(dataLookup && dataLookupName == null) { 1150 | dataLookupName = child.getName(); 1151 | } 1152 | context = ((Map)context).get(child.getName()); 1153 | if(variableLookup && context == null) { 1154 | if(result.get("variable") != null && result.get("variable") instanceof Map) { 1155 | Map variableDefinitions = (Map)( result.get("variable")); 1156 | if(variableDefinitions.get(child.getName()) instanceof Map) { 1157 | Map varDefinition = (Map)(variableDefinitions.get(child.getName())); 1158 | if(varDefinition != null) { 1159 | return varDefinition.get("default"); 1160 | } else { 1161 | return null; 1162 | } 1163 | } else { 1164 | return variableDefinitions.get(child.getName()); 1165 | } 1166 | 1167 | 1168 | } 1169 | } 1170 | } else { 1171 | //if we have not cached a data lookup call we run it this way 1172 | if(dataLookup && dataLookupName == null) { 1173 | dataLookupName = child.getName(); 1174 | } else if (dataLookup && dataLookupName != null) { 1175 | if(result.get("data") != null && result.get("data") instanceof Map) { 1176 | Map dataDefinitions = (Map)(result.get("data")); 1177 | if(dataDefinitions.get(dataLookupName) instanceof Map) { 1178 | Map dataLookupDefinition = (Map) dataDefinitions.get(dataLookupName); 1179 | if(dataLookupDefinition.get(child.getName()) instanceof Map) { 1180 | //we have a data lookup definition we need to run 1181 | context = evaluateDataLookup(dataLookupName,child.getName(),(Map)(dataLookupDefinition.get(child.getName()))); 1182 | } 1183 | } 1184 | } 1185 | } 1186 | } 1187 | 1188 | } else if(child instanceof HCLArray) { 1189 | //TODO: If there are more elements throw exception 1190 | Symbol firstElement = child.getChildren().get(0); 1191 | Object elementResult = null; 1192 | if(firstElement instanceof EvalSymbol) { 1193 | elementResult = processEvaluation((EvalSymbol) firstElement,null); 1194 | } else if(firstElement instanceof HCLValue) { 1195 | elementResult = processValue((HCLValue)firstElement); 1196 | } 1197 | 1198 | if(elementResult != null && elementResult instanceof String && context != null) { 1199 | context = ((Map)context).get(elementResult); 1200 | if(variableLookup && context == null ) { 1201 | if(result.get("variable") != null && result.get("variable") instanceof Map) { 1202 | Map variableDefinitions = (Map)( result.get("variable")); 1203 | if(variableDefinitions.get(elementResult) instanceof Map) { 1204 | Map varDefinition = (Map)(variableDefinitions.get(elementResult)); 1205 | if(varDefinition != null) { 1206 | return varDefinition.get("default"); 1207 | } else { 1208 | return null; 1209 | } 1210 | } else { 1211 | return variableDefinitions.get(elementResult); 1212 | } 1213 | 1214 | 1215 | } 1216 | } 1217 | 1218 | } else if (elementResult != null && elementResult instanceof Double && context != null) { 1219 | //index position 1220 | Double elementDouble = (Double)elementResult; 1221 | if(context instanceof Map) { 1222 | context = ((Map)context).get(elementDouble.intValue()); 1223 | } else { 1224 | context = ((List)context).get(elementDouble.intValue()); 1225 | } 1226 | } else { 1227 | context = null; 1228 | } 1229 | 1230 | } 1231 | } 1232 | if(context != null) { 1233 | if(context instanceof HCLAttribute) { 1234 | LinkedHashMap nestedMap = new LinkedHashMap<>(); 1235 | context = processSymbolPass2(context,nestedMap); 1236 | //not my favorite way to grab this value but works for now 1237 | for(String key : nestedMap.keySet()) { 1238 | context = nestedMap.get(key); 1239 | } 1240 | } 1241 | return context; 1242 | } else { 1243 | return evalSymbol; 1244 | } 1245 | 1246 | } 1247 | else if(evalSymbol instanceof Function) { 1248 | Function thisFunction = (Function)evalSymbol; 1249 | if(thisFunction.getName() != null && thisFunction.getName().length() > 0) { 1250 | return evaluateFunctionCall(thisFunction.getName(),thisFunction,stackVars); 1251 | } else { 1252 | return null; 1253 | } 1254 | } else if(evalSymbol instanceof ComputedTuple) { 1255 | //time to actually implemented a tuple loop 1256 | ComputedTuple thisTuple = (ComputedTuple) evalSymbol; 1257 | return evaluateComputedTuple(thisTuple,stackVars); 1258 | } 1259 | else if(evalSymbol instanceof Variable) { 1260 | System.out.println("Evaluating Variable: " + evalSymbol.getName()); 1261 | if(stackVars != null) { 1262 | context = stackVars.get(evalSymbol.getName()); 1263 | if(context == null) { 1264 | context = result.get(evalSymbol.getName()); 1265 | } 1266 | } else { 1267 | context = result.get(evalSymbol.getName()); 1268 | } 1269 | return context; 1270 | } 1271 | else if(evalSymbol instanceof ComputedObject) { 1272 | return evalSymbol; 1273 | } else { 1274 | return null; 1275 | } 1276 | } 1277 | 1278 | protected Object flattenContext(Object context) { 1279 | if(context == null) { 1280 | return null; 1281 | } else if(context instanceof Collection) { 1282 | LinkedHashMap contextMap = new LinkedHashMap<>(); 1283 | for(Object contextObj : (Collection)context) { 1284 | if(contextObj instanceof Map) { 1285 | Map currentContextMap = (Map)contextObj; 1286 | for(String key : currentContextMap.keySet()) { 1287 | contextMap.put(key,currentContextMap.get(key)); 1288 | } 1289 | } 1290 | } 1291 | return contextMap; 1292 | } else { 1293 | return context; 1294 | } 1295 | } 1296 | } 1297 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/HCLParserException.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j; 2 | 3 | /** 4 | * When a HCL Lexical parser error is reached this exception is thrown. 5 | * @author David Estes 6 | */ 7 | public class HCLParserException extends Exception { 8 | public HCLParserException(String message) { 9 | super(message); 10 | } 11 | public HCLParserException(String message,Exception ex) { 12 | super(message,ex); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/AnyPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class AnyPrimitiveType extends PrimitiveType { 4 | public AnyPrimitiveType(Integer line, Integer column, Long position) { 5 | super("any",line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "AnyPrimitiveType"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/BooleanPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class BooleanPrimitiveType extends PrimitiveType { 4 | public BooleanPrimitiveType(Integer line, Integer column, Long position) { 5 | super("boolean",line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "BooleanPrimitiveType"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/ComputedObject.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class ComputedObject extends EvalSymbol { 6 | public ComputedObject(String name, Integer line, Integer column, Long position) { 7 | super(name,line,column,position); 8 | this.variables = new ArrayList<>(); 9 | } 10 | 11 | private EvalSymbol sourceExpression; 12 | private ArrayList variables; 13 | private EvalSymbol targetExpression; 14 | private EvalSymbol conditionalExpression; 15 | 16 | 17 | public String getSymbolName() { 18 | return "ComputedObject"; 19 | } 20 | 21 | public EvalSymbol getSourceExpression() { 22 | return sourceExpression; 23 | } 24 | 25 | public void setSourceExpression(EvalSymbol sourceExpression) { 26 | this.sourceExpression = sourceExpression; 27 | } 28 | 29 | public ArrayList getVariables() { 30 | return variables; 31 | } 32 | 33 | public void setVariables(ArrayList variables) { 34 | this.variables = variables; 35 | } 36 | 37 | public EvalSymbol getTargetExpression() { 38 | return targetExpression; 39 | } 40 | 41 | public void setTargetExpression(EvalSymbol targetExpression) { 42 | this.targetExpression = targetExpression; 43 | } 44 | 45 | public EvalSymbol getConditionalExpression() { 46 | return conditionalExpression; 47 | } 48 | 49 | public void setConditionalExpression(EvalSymbol conditionalExpression) { 50 | this.conditionalExpression = conditionalExpression; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/ComputedTuple.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class ComputedTuple extends EvalSymbol { 6 | 7 | public ComputedTuple(String name, Integer line, Integer column, Long position) { 8 | super(name,line,column,position); 9 | this.variables = new ArrayList<>(); 10 | } 11 | 12 | private EvalSymbol sourceExpression; 13 | private ArrayList variables; 14 | private EvalSymbol targetExpression; 15 | private EvalSymbol conditionalExpression; 16 | 17 | 18 | public String getSymbolName() { 19 | return "ComputedTuple"; 20 | } 21 | 22 | public EvalSymbol getSourceExpression() { 23 | return sourceExpression; 24 | } 25 | 26 | public void setSourceExpression(EvalSymbol sourceExpression) { 27 | this.sourceExpression = sourceExpression; 28 | } 29 | 30 | public ArrayList getVariables() { 31 | return variables; 32 | } 33 | 34 | public void setVariables(ArrayList variables) { 35 | this.variables = variables; 36 | } 37 | 38 | public EvalSymbol getTargetExpression() { 39 | return targetExpression; 40 | } 41 | 42 | public void setTargetExpression(EvalSymbol targetExpression) { 43 | this.targetExpression = targetExpression; 44 | } 45 | 46 | public EvalSymbol getConditionalExpression() { 47 | return conditionalExpression; 48 | } 49 | 50 | public void setConditionalExpression(EvalSymbol conditionalExpression) { 51 | this.conditionalExpression = conditionalExpression; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/EvalSymbol.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | import com.bertramlabs.plugins.hcl4j.symbols.GenericSymbol; 4 | 5 | public class EvalSymbol extends GenericSymbol { 6 | @Override 7 | public String getSymbolName() { 8 | return "EvaluationSymbol"; 9 | } 10 | 11 | public EvalSymbol(String name, Integer line, Integer column, Long position) { 12 | super(name,line,column,position); 13 | } 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/ForConditional.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class ForConditional extends GroupedExpression{ 4 | public ForConditional(Integer line, Integer column, Long position) { 5 | super(line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "ForConditional"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/ForSource.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class ForSource extends EvalSymbol{ 4 | public ForSource(Integer line, Integer column, Long position) { 5 | super(null,line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "ForSource"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/Function.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class Function extends EvalSymbol{ 6 | public Function(String name, Integer line, Integer column, Long position) { 7 | super(name,line,column,position); 8 | } 9 | 10 | public String getSymbolName() { 11 | return "Function"; 12 | } 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/GroupedExpression.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | import com.bertramlabs.plugins.hcl4j.symbols.GenericSymbol; 4 | 5 | public class GroupedExpression extends GenericSymbol { 6 | public GroupedExpression(Integer line, Integer column, Long position) { 7 | super(null,line,column,position); 8 | } 9 | 10 | public String getSymbolName() { 11 | return "GroupedExpression"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/ListExpr.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class ListExpr extends EvalSymbol{ 4 | public ListExpr(String name, Integer line, Integer column, Long position) { 5 | super(name,line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "ListExpr"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/ListPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class ListPrimitiveType extends SubTypePrimitiveType { 4 | 5 | public ListPrimitiveType(PrimitiveType subType, Integer line, Integer column, Long position) { 6 | super(subType,"list",line,column,position); 7 | } 8 | 9 | public String getSymbolName() { 10 | return "ListPrimitiveType"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/MapPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class MapPrimitiveType extends SubTypePrimitiveType { 4 | 5 | public MapPrimitiveType(PrimitiveType subType, Integer line, Integer column, Long position) { 6 | super(subType,"map",line,column,position); 7 | } 8 | 9 | public String getSymbolName() { 10 | return "MapPrimitiveType"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/NumberPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class NumberPrimitiveType extends PrimitiveType { 4 | public NumberPrimitiveType(Integer line, Integer column, Long position) { 5 | super("number",line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "NumberPrimitiveType"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/Operator.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class Operator extends EvalSymbol { 4 | public Operator(String name, Integer line, Integer column, Long position) { 5 | super(name,line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "Operator"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/PrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class PrimitiveType extends EvalSymbol { 4 | public PrimitiveType(String name, Integer line, Integer column, Long position) { 5 | super(name,line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "PrimitiveType"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/SetPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class SetPrimitiveType extends SubTypePrimitiveType { 4 | 5 | public SetPrimitiveType(PrimitiveType subType, Integer line, Integer column, Long position) { 6 | super(subType,"set",line,column,position); 7 | } 8 | 9 | public String getSymbolName() { 10 | return "SetPrimitiveType"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/StringInterpolatedExpression.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | import com.bertramlabs.plugins.hcl4j.symbols.GenericSymbol; 4 | 5 | public class StringInterpolatedExpression extends GenericSymbol { 6 | public StringInterpolatedExpression(Integer line, Integer column, Long position) { 7 | super(null,line,column,position); 8 | } 9 | 10 | public String getSymbolName() { 11 | return "StringInterpolatedExpression"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/StringPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class StringPrimitiveType extends PrimitiveType { 4 | public StringPrimitiveType(Integer line, Integer column, Long position) { 5 | super("string",line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "StringPrimitiveType"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/SubTypePrimitiveType.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class SubTypePrimitiveType extends PrimitiveType { 4 | 5 | /** 6 | * This field is no longer used as it was silly to not just use the existing Child Tree. as of 0.6.0 this is no 7 | * longer used. please reference the getChildren() method for child types if exists. 8 | * @deprecated 9 | */ 10 | protected PrimitiveType subType; 11 | 12 | public SubTypePrimitiveType(PrimitiveType subType,String name, Integer line, Integer column, Long position) { 13 | super(name,line,column,position); 14 | this.subType = subType; 15 | } 16 | 17 | public PrimitiveType getSubType() { 18 | if(this.getChildren().size() > 0) { 19 | if(this.getChildren().get(0) instanceof SubTypePrimitiveType) { 20 | return (SubTypePrimitiveType)(this.getChildren().get(0)); 21 | } 22 | } 23 | return null; 24 | } 25 | 26 | public String getSymbolName() { 27 | return "SubTypePrimitiveType"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/Variable.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | public class Variable extends EvalSymbol { 4 | public Variable(String name, Integer line, Integer column, Long position) { 5 | super(name,line,column,position); 6 | } 7 | 8 | public String getSymbolName() { 9 | return "Variable"; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/RuntimeSymbols/VariableTree.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.RuntimeSymbols; 2 | 3 | import com.bertramlabs.plugins.hcl4j.symbols.GenericSymbol; 4 | import com.bertramlabs.plugins.hcl4j.symbols.HCLArray; 5 | import com.bertramlabs.plugins.hcl4j.symbols.Symbol; 6 | 7 | import java.util.ArrayList; 8 | 9 | public class VariableTree extends Variable { 10 | public VariableTree(Integer line, Integer column, Long position) { 11 | super(null,line,column,position); 12 | } 13 | 14 | public String getSymbolName() { 15 | return "VariableTree"; 16 | } 17 | 18 | public String getName() { 19 | ArrayList elementNames = new ArrayList<>(); 20 | GenericSymbol currentSymbol = this; 21 | for(Symbol child : currentSymbol.getChildren()) { 22 | if(child instanceof HCLArray) { 23 | if(child.getChildren().size() > 0) { 24 | elementNames.add(child.getChildren().get(0).getName()); 25 | } else { 26 | elementNames.add(child.getName()); 27 | } 28 | } else { 29 | elementNames.add(child.getName()); 30 | } 31 | 32 | } 33 | return String.join(".",elementNames); 34 | } 35 | 36 | public String toString() { 37 | return getName(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/symbols/GenericSymbol.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j.symbols; 17 | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; 19 | 20 | import java.util.List; 21 | import java.util.ArrayList; 22 | 23 | public abstract class GenericSymbol implements Symbol { 24 | private Integer line; 25 | private Integer column; 26 | private Long position; 27 | private Integer length; 28 | 29 | private String name; 30 | 31 | private List children = new ArrayList(); 32 | private List attributes = new ArrayList(); 33 | 34 | @JsonIgnore 35 | private Symbol parent; 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public Integer getLine() { 46 | return line; 47 | } 48 | 49 | public Integer getLength() { 50 | return length; 51 | } 52 | 53 | public void setLength(Integer length) { 54 | this.length = length; 55 | } 56 | 57 | public Integer getColumn() { 58 | return column; 59 | } 60 | 61 | public Long getPosition() { 62 | return position; 63 | } 64 | 65 | public List getChildren() { 66 | return children; 67 | } 68 | 69 | public List getAttributes() { 70 | return attributes; 71 | } 72 | 73 | public Symbol getParent() { 74 | return parent; 75 | } 76 | 77 | public void setParent(Symbol symbol) { 78 | this.parent = symbol; 79 | } 80 | 81 | public void appendChild(Symbol symbol) { 82 | children.add(symbol); symbol.setParent(this); 83 | } 84 | 85 | public void appendAttribute(Symbol symbol) { 86 | attributes.add(symbol); 87 | } 88 | 89 | public GenericSymbol(String name) { 90 | this.name = name; 91 | } 92 | 93 | public GenericSymbol(String name,Integer line, Integer column, Long position) { 94 | this.name = name; 95 | this.line = line; 96 | this.column = column; 97 | this.position = position; 98 | } 99 | 100 | public String toString() { 101 | return getSymbolName() + ":" + getName(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/symbols/HCLArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j.symbols; 17 | 18 | 19 | public class HCLArray extends GenericSymbol { 20 | 21 | public String getSymbolName() { 22 | return "Array"; 23 | } 24 | 25 | public HCLArray(Integer line, Integer column, Long position) { 26 | super("array",line,column,position); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/symbols/HCLAttribute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j.symbols; 17 | 18 | import com.bertramlabs.plugins.hcl4j.RuntimeSymbols.StringInterpolatedExpression; 19 | 20 | public class HCLAttribute extends GenericSymbol { 21 | 22 | public StringInterpolatedExpression runtimeName = null; 23 | public HCLAttribute(String name, Integer line, Integer column, Long position) { 24 | super(name,line,column,position); 25 | } 26 | 27 | public String getSymbolName() { 28 | return "Attribute"; 29 | } 30 | 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/symbols/HCLBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j.symbols; 17 | 18 | import java.util.List; 19 | 20 | public class HCLBlock extends GenericSymbol { 21 | 22 | public HCLBlock(List blockNames, Symbol parent,Integer line, Integer column, Long position) { 23 | super(blockNames.get(0),line,column,position); 24 | this.blockNames = blockNames; 25 | this.setParent(parent); 26 | } 27 | 28 | public String getSymbolName() { 29 | return "Block"; 30 | } 31 | 32 | public List blockNames; 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/symbols/HCLMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j.symbols; 17 | 18 | import java.util.LinkedHashMap; 19 | import java.util.Map; 20 | 21 | public class HCLMap extends GenericSymbol { 22 | 23 | 24 | public HCLMap(Integer line, Integer column, Long position) { 25 | super("array",line,column,position); 26 | } 27 | 28 | 29 | public String getSymbolName() { 30 | return "Map"; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/symbols/HCLValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j.symbols; 17 | 18 | /** 19 | * A Class representation of an attributes value. This could be a generic type like a "string", "number", "boolean", "array" (see {@link HCLArray}, or "map" (see {@link HCLMap}). 20 | * This is an internal parser lexer class and should not be needed externally. 21 | * @author David Estes 22 | */ 23 | public class HCLValue extends GenericSymbol { 24 | public String type; 25 | public Object value; 26 | public HCLValue parent; 27 | 28 | public String getSymbolName() { 29 | return "Value"; 30 | } 31 | 32 | 33 | public HCLValue(String type, Object value, Integer line, Integer column, Long position) { 34 | super("value",line,column,position); 35 | this.type = type; 36 | this.value = value; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/symbols/Symbol.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j.symbols; 17 | 18 | import java.util.List; 19 | 20 | public interface Symbol { 21 | public String getSymbolName(); 22 | 23 | Integer getLine(); 24 | Integer getColumn(); 25 | Long getPosition(); 26 | Integer getLength(); 27 | void setLength(Integer length); 28 | 29 | String getName(); 30 | void setName(String name); 31 | 32 | List getAttributes(); 33 | void appendAttribute(Symbol symbol); 34 | 35 | List getChildren(); 36 | void appendChild(Symbol symbol); 37 | 38 | Symbol getParent(); 39 | void setParent(Symbol parent); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/utils/HttpApiClient.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.utils; 2 | 3 | 4 | import org.apache.commons.beanutils.PropertyUtils; 5 | import org.apache.http.*; 6 | import org.apache.http.auth.AuthScope; 7 | import org.apache.http.auth.NTCredentials; 8 | import org.apache.http.client.config.CookieSpecs; 9 | import org.apache.http.entity.InputStreamEntity; 10 | import org.apache.http.impl.client.ProxyAuthenticationStrategy; 11 | import org.apache.http.client.HttpClient; 12 | import org.apache.http.client.entity.UrlEncodedFormEntity; 13 | import org.apache.http.client.methods.*; 14 | import org.apache.http.client.utils.URIBuilder; 15 | import org.apache.http.config.MessageConstraints; 16 | import org.apache.http.config.Registry; 17 | import org.apache.http.config.RegistryBuilder; 18 | import org.apache.http.conn.ConnectTimeoutException; 19 | import org.apache.http.client.config.RequestConfig; 20 | import org.apache.http.conn.HttpClientConnectionManager; 21 | import org.apache.http.conn.HttpConnectionFactory; 22 | import org.apache.http.conn.ManagedHttpClientConnection; 23 | import org.apache.http.conn.routing.HttpRoute; 24 | import org.apache.http.conn.socket.ConnectionSocketFactory; 25 | import org.apache.http.conn.socket.PlainConnectionSocketFactory; 26 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 27 | import org.apache.http.conn.ssl.SSLContextBuilder; 28 | import org.apache.http.conn.ssl.TrustStrategy; 29 | import org.apache.http.conn.ssl.X509HostnameVerifier; 30 | import org.apache.http.entity.ByteArrayEntity; 31 | import org.apache.http.entity.StringEntity; 32 | import org.apache.http.entity.ContentType; 33 | import org.apache.http.impl.DefaultHttpResponseFactory; 34 | import org.apache.http.impl.client.BasicCookieStore; 35 | import org.apache.http.impl.client.BasicCredentialsProvider; 36 | import org.apache.http.client.CredentialsProvider; 37 | import org.apache.http.impl.client.HttpClientBuilder; 38 | import org.apache.http.impl.client.HttpClients; 39 | import org.apache.http.impl.conn.BasicHttpClientConnectionManager; 40 | import org.apache.http.impl.conn.DefaultHttpResponseParser; 41 | import org.apache.http.impl.conn.DefaultHttpResponseParserFactory; 42 | import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory; 43 | import org.apache.http.entity.mime.MultipartEntityBuilder; 44 | import org.apache.http.entity.mime.content.StringBody; 45 | import org.apache.http.impl.cookie.BasicClientCookie; 46 | import org.apache.http.impl.io.DefaultHttpRequestWriterFactory; 47 | import org.apache.http.io.HttpMessageParser; 48 | import org.apache.http.io.HttpMessageParserFactory; 49 | import org.apache.http.io.HttpMessageWriterFactory; 50 | import org.apache.http.io.SessionInputBuffer; 51 | import org.apache.http.message.BasicHeader; 52 | import org.apache.http.message.BasicNameValuePair; 53 | import org.apache.http.message.BasicLineParser; 54 | import org.apache.http.message.LineParser; 55 | import org.apache.http.protocol.HttpContext; 56 | import org.apache.http.ssl.SSLContexts; 57 | import org.apache.http.util.CharArrayBuffer; 58 | import org.apache.http.util.EntityUtils; 59 | import org.slf4j.Logger; 60 | import org.slf4j.LoggerFactory; 61 | 62 | import javax.net.ssl.*; 63 | import java.io.IOException; 64 | import java.io.InputStream; 65 | import java.lang.reflect.InvocationTargetException; 66 | import java.net.InetSocketAddress; 67 | import java.net.Socket; 68 | import java.net.URISyntaxException; 69 | import java.security.KeyManagementException; 70 | import java.security.KeyStoreException; 71 | import java.security.NoSuchAlgorithmException; 72 | import java.security.cert.X509Certificate; 73 | import java.util.*; 74 | import java.util.Map; 75 | import org.xml.sax.SAXParseException; 76 | 77 | /** 78 | * Utility methods for calling external APIs in a standardized way. 79 | * 80 | * @author David Estes 81 | * @since 0.8.0 82 | */ 83 | public class HttpApiClient { 84 | 85 | HttpClient httpClient; 86 | HttpClientConnectionManager connectionManager; 87 | BasicCookieStore cookieStore = new BasicCookieStore(); 88 | /** 89 | * Sets a throttle rate (in milliseconds) between HTTP Calls. This is used to slow down queries to the remote server. 90 | */ 91 | public Long throttleRate = 0L; 92 | 93 | private Date lastCallTime; 94 | 95 | static Logger log = LoggerFactory.getLogger(HttpApiClient.class); 96 | 97 | static final Integer WEB_CONNECTION_TIMEOUT = 120 * 1000; 98 | 99 | public ServiceResponse callApi(String url, String path, String username, String password) throws URISyntaxException, Exception { 100 | return callApi(url,path,username,password,new RequestOptions(),"POST"); 101 | } 102 | 103 | public ServiceResponse callApi(String url, String path, String username, String password, RequestOptions opts) throws URISyntaxException, Exception { 104 | return callApi(url,path,username,password,opts,"POST"); 105 | } 106 | 107 | private void sleepIfNecessary() { 108 | try { 109 | Long tmpThrottleRate = throttleRate; 110 | if(tmpThrottleRate != null && tmpThrottleRate > 0) { 111 | if(lastCallTime != null) { 112 | Date now = new Date(); 113 | Long timeDiff = now.getTime() - lastCallTime.getTime(); 114 | tmpThrottleRate = tmpThrottleRate - timeDiff; 115 | if(tmpThrottleRate > 0) { 116 | Thread.sleep(tmpThrottleRate); 117 | } 118 | } else { 119 | Thread.sleep(tmpThrottleRate); 120 | } 121 | 122 | } 123 | } catch(InterruptedException ignore) { 124 | 125 | } 126 | 127 | } 128 | 129 | public ServiceResponse callApi(String url, final String path, String username, String password, RequestOptions opts, String method) throws URISyntaxException, Exception { 130 | ServiceResponse rtn = new ServiceResponse(); 131 | LinkedHashMap data = new LinkedHashMap<>(); 132 | rtn.setData(data); 133 | URIBuilder uriBuilder = new URIBuilder(url); 134 | try { 135 | 136 | sleepIfNecessary(); 137 | lastCallTime = new Date(); 138 | 139 | String existingPath = uriBuilder.getPath(); 140 | // retain path on base url if one exists 141 | String newPath = path; 142 | if(path != null && path.length() > 0) { 143 | if(existingPath != null && existingPath.length() > 0 && !path.startsWith(existingPath)) { 144 | if(existingPath.endsWith("/") && path.startsWith("/")) { 145 | existingPath = existingPath.substring(0, existingPath.length() - 1); 146 | } else if(!existingPath.endsWith("/") && !path.startsWith("/")) { 147 | existingPath += "/"; 148 | } 149 | newPath = existingPath + path; 150 | } 151 | uriBuilder.setPath(newPath); 152 | } 153 | if(opts.queryParams != null && !opts.queryParams.isEmpty()) { 154 | for(CharSequence queryKey : opts.queryParams.keySet()) { 155 | uriBuilder.addParameter(queryKey.toString(), opts.queryParams.get(queryKey).toString()); 156 | } 157 | } 158 | 159 | HttpRequestBase request; 160 | switch(method.toUpperCase()) { 161 | case "HEAD": 162 | request = new HttpHead(uriBuilder.build()); 163 | break; 164 | case "PUT": 165 | request = new HttpPut(uriBuilder.build()); 166 | break; 167 | case "POST": 168 | request = new HttpPost(uriBuilder.build()); 169 | break; 170 | case "PATCH": 171 | request = new HttpPatch(uriBuilder.build()); 172 | break; 173 | case "GET": 174 | request = new HttpGet(uriBuilder.build()); 175 | break; 176 | case "DELETE": 177 | request = new HttpDelete(uriBuilder.build()); 178 | break; 179 | default: 180 | throw new Exception("method was not specified"); 181 | } 182 | if(username != null && username.length() > 0 && password != null && password.length() > 0) { 183 | String creds = username + ":" + password; 184 | String credHeader = "Basic " + Base64.getEncoder().encodeToString(creds.getBytes()); 185 | request.addHeader("Authorization",credHeader); 186 | } 187 | 188 | //if bearer token 189 | if(opts.apiToken != null) { 190 | int newLine = opts.apiToken.indexOf('\n'); 191 | if(newLine > -1) 192 | opts.apiToken = opts.apiToken.substring(0, newLine); 193 | request.addHeader("Authorization", "Bearer " + opts.apiToken); 194 | } 195 | 196 | 197 | // Headers 198 | if(opts.headers == null || opts.headers.isEmpty() || !opts.headers.containsKey("Content-Type")) { 199 | request.addHeader("Content-Type", "application/json"); 200 | } 201 | 202 | if(opts.headers != null && !opts.headers.isEmpty()) { 203 | for (CharSequence headerKey : opts.headers.keySet()) { 204 | request.addHeader(headerKey.toString(), opts.headers.get(headerKey).toString()); 205 | } 206 | } 207 | 208 | if (opts.body != null) { 209 | HttpEntityEnclosingRequestBase postRequest = (HttpEntityEnclosingRequestBase) request; 210 | if(opts.body instanceof Map) { 211 | if (opts.contentType == "form") { 212 | List urlParameters = new ArrayList(); 213 | Map bodyMap = (Map) opts.body; 214 | for (String key : bodyMap.keySet()) { 215 | Object v = bodyMap.get(key); 216 | Object rowValue; 217 | if (v instanceof CharSequence) { 218 | rowValue = v; 219 | } else { 220 | rowValue = v.toString(); 221 | } 222 | urlParameters.add(new BasicNameValuePair(key, rowValue.toString())); 223 | } 224 | postRequest.setEntity(new UrlEncodedFormEntity(urlParameters)); 225 | } else if (opts.contentType == "multi-part-form") { 226 | MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); 227 | String rowBoundary = "--" + java.util.UUID.randomUUID().toString() + "--"; 228 | Map bodyMap = (Map) opts.body; 229 | for (String key : bodyMap.keySet()) { 230 | Object v = bodyMap.get(key); 231 | //if multiples.. 232 | if (v instanceof Collection) { 233 | for (String rowValue : (Collection) v) { 234 | StringBody rowBody = new StringBody(rowValue.toString(), ContentType.create("text/plain", "UTF-8")); 235 | entityBuilder.addPart(key, rowBody); 236 | } 237 | } else { 238 | Object rowValue; 239 | //convert it 240 | if (v instanceof CharSequence) { 241 | rowValue = v; 242 | } else { 243 | rowValue = v.toString(); 244 | } 245 | StringBody rowBody = new StringBody(rowValue.toString(), ContentType.create("text/plain", "UTF-8")); 246 | entityBuilder.addPart(key, rowBody); 247 | } 248 | } 249 | entityBuilder.setContentType(ContentType.MULTIPART_FORM_DATA); 250 | entityBuilder.setBoundary(rowBoundary); 251 | postRequest.setEntity(entityBuilder.build()); 252 | //replace the header 253 | if (request.containsHeader("Content-Type")) { 254 | //append the boundary 255 | Header currentType = request.getFirstHeader("Content-Type"); 256 | String newValue = currentType.getValue(); 257 | newValue = newValue + "; boundary=" + rowBoundary; 258 | request.setHeader("Content-Type", newValue); 259 | } 260 | } 261 | } else if(opts.body instanceof byte[]) { 262 | postRequest.setEntity(new ByteArrayEntity((byte[]) opts.body)); 263 | } else if(opts.body instanceof InputStream) { 264 | postRequest.setEntity(new InputStreamEntity((InputStream)(opts.body), opts.contentLength != null ? opts.contentLength : -1)); 265 | } else { 266 | postRequest.setEntity(new StringEntity(opts.body.toString())); 267 | } 268 | } 269 | 270 | withClient(opts,(HttpClient client, BasicCookieStore cookieStore) -> { 271 | 272 | CloseableHttpResponse response = null; 273 | try { 274 | 275 | response = (CloseableHttpResponse)client.execute(request); 276 | if(response.getStatusLine().getStatusCode() <= 399) { 277 | for(Header header : response.getAllHeaders()) { 278 | rtn.addHeader(header.getName(), header.getValue()); 279 | } 280 | 281 | 282 | for(Header header : response.getHeaders("Set-Cookie")) { 283 | Map cookies = extractCookie(header.getValue()); 284 | for(String cookieKey : cookies.keySet()) { 285 | BasicClientCookie cookie = new BasicClientCookie(cookieKey, cookies.get(cookieKey)); 286 | cookie.setPath("/"); 287 | cookie.setDomain(request.getURI().getHost()); 288 | cookieStore.addCookie(cookie); 289 | } 290 | } 291 | 292 | 293 | HttpEntity entity = response.getEntity(); 294 | if(entity != null) { 295 | rtn.setContent(EntityUtils.toString(entity)); 296 | if(!opts.suppressLog) { 297 | log.debug("results of SUCCESSFUL call to {}/{}, results: {}",url,path,rtn.getContent()); 298 | } 299 | } else { 300 | rtn.setContent(null); 301 | } 302 | rtn.setErrorCode(Integer.toString(response.getStatusLine().getStatusCode())); 303 | rtn.setSuccess(true); 304 | } else { 305 | if(response.getEntity() != null) { 306 | rtn.setContent(EntityUtils.toString(response.getEntity())); 307 | } 308 | rtn.setSuccess(false); 309 | rtn.setErrorCode(Integer.toString(response.getStatusLine().getStatusCode())); 310 | log.warn("path: {} error: {} - {}",path,rtn.getErrorCode(),rtn.getContent()); 311 | } 312 | } catch(Exception ex) { 313 | try { 314 | log.error("Error occurred processing the response for {} : {}",uriBuilder.build().toString(),ex.getMessage(), ex); 315 | rtn.setError("Error occurred processing the response for " + uriBuilder.build().toString() + " : " + ex.getMessage()); 316 | } catch(URISyntaxException uie) { 317 | log.error("Error occurred processing the response for {} : {}","invalid uri",ex.getMessage(), ex); 318 | rtn.setError("Error occurred processing the response for invalid uri : " + ex.getMessage()); 319 | 320 | } 321 | 322 | rtn.setSuccess(false); 323 | } finally { 324 | lastCallTime = new Date(); 325 | if(response != null) { 326 | try { 327 | response.close(); 328 | } catch (IOException ignored) { 329 | //ignored exception 330 | } 331 | } 332 | } 333 | }); 334 | 335 | } catch(SSLProtocolException sslEx) { 336 | log.error("Error Occurred calling API (SSL Exception): {}",sslEx.getMessage(),sslEx); 337 | rtn.addError("sslHandshake", "SSL Handshake Exception (is SNI Misconfigured): " + sslEx.getMessage()); 338 | rtn.setSuccess(false); 339 | } catch (Exception e) { 340 | log.error("Error Occurred calling API: " + e.getMessage(),e); 341 | rtn.addError("error", e.getMessage()); 342 | rtn.setSuccess(false); 343 | } 344 | return rtn; 345 | } 346 | 347 | 348 | public Map addRequiredHeader(Map headers, String name, String value) { 349 | if(headers == null) { 350 | headers = new LinkedHashMap<>(); 351 | } 352 | headers.putIfAbsent(name,value); 353 | return headers; 354 | } 355 | 356 | Map extractCookie(String rawCookie) { 357 | if(rawCookie == null || rawCookie.length() == 0) return null; 358 | String[] cookieArgs = rawCookie.split("="); 359 | String name = cookieArgs[0]; 360 | String data = rawCookie.split(name + "=")[1].split(";")[0]; 361 | String value = ""; 362 | if(data != null && data.length() > 0) { 363 | value = data.substring(1,data.length() - 1); 364 | } 365 | Map cookieMap = new LinkedHashMap<>(); 366 | cookieMap.put(name,value); 367 | return cookieMap; 368 | } 369 | 370 | private void withClient(RequestOptions opts, WithClientFunction withClientFunction) { 371 | 372 | Boolean ignoreSSL = opts.ignoreSSL; 373 | if(httpClient != null) { 374 | withClientFunction.method(httpClient,cookieStore); 375 | return; 376 | } else { 377 | HttpClientBuilder clientBuilder = HttpClients.custom(); 378 | 379 | 380 | RequestConfig.Builder reqConfigBuilder = RequestConfig.custom(); 381 | reqConfigBuilder.setCookieSpec(CookieSpecs.STANDARD); 382 | if(opts.connectionTimeout != null) { 383 | reqConfigBuilder.setConnectTimeout(opts.connectionTimeout); 384 | reqConfigBuilder.setConnectionRequestTimeout(opts.connectionTimeout); 385 | } 386 | if(opts.readTimeout != null) { 387 | reqConfigBuilder.setSocketTimeout(opts.readTimeout); 388 | } 389 | clientBuilder.setDefaultRequestConfig(reqConfigBuilder.build()); 390 | 391 | 392 | clientBuilder.setDefaultCookieStore(cookieStore); 393 | clientBuilder.setHostnameVerifier(new X509HostnameVerifier() { 394 | public boolean verify(String host, SSLSession sess) { 395 | return true; 396 | } 397 | 398 | public void verify(String host, SSLSocket ssl) { 399 | 400 | } 401 | 402 | public void verify(String host, String[] cns, String[] subjectAlts) { 403 | 404 | } 405 | 406 | public void verify(String host, X509Certificate cert) { 407 | 408 | } 409 | 410 | }); 411 | SSLConnectionSocketFactory sslConnectionFactory; 412 | SSLContext sslcontext = null; 413 | if(ignoreSSL) { 414 | try { 415 | sslcontext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { 416 | @Override 417 | public boolean isTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { 418 | return true; 419 | } 420 | }).build(); 421 | } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException ignored) { 422 | //; 423 | } 424 | sslConnectionFactory = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) { 425 | 426 | @Override 427 | protected void prepareSocket(SSLSocket socket) { 428 | try { 429 | PropertyUtils.setProperty(socket, "host", null); 430 | } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 431 | e.printStackTrace(); 432 | } 433 | List serverNames = Collections. emptyList(); 434 | SSLParameters sslParams = socket.getSSLParameters(); 435 | sslParams.setServerNames(serverNames); 436 | socket.setSSLParameters(sslParams); 437 | } 438 | @Override 439 | public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException, ConnectTimeoutException { 440 | if(socket instanceof SSLSocket) { 441 | try { 442 | String[] enabledProtocols = {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}; 443 | SSLSocket sslSocket = (SSLSocket)socket; 444 | sslSocket.setEnabledProtocols(enabledProtocols); 445 | PropertyUtils.setProperty(socket, "host", host.getHostName()); 446 | } catch (Exception ex) { 447 | log.error("We have an unhandled exception when attempting to connect to {} ignoring SSL errors",host, ex); 448 | } 449 | } 450 | return super.connectSocket(WEB_CONNECTION_TIMEOUT, socket, host, remoteAddress, localAddress, context); 451 | } 452 | }; 453 | } else { 454 | sslcontext = SSLContexts.createSystemDefault(); 455 | sslConnectionFactory = new SSLConnectionSocketFactory(sslcontext) { 456 | @Override 457 | public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException, ConnectTimeoutException { 458 | if(socket instanceof SSLSocket) { 459 | try { 460 | String[] enabledProtocols = {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}; 461 | SSLSocket sslSocket = (SSLSocket)socket; 462 | sslSocket.setEnabledProtocols(enabledProtocols); 463 | PropertyUtils.setProperty(socket, "host", host.getHostName()); 464 | } catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { 465 | //; 466 | } 467 | } 468 | 469 | return super.connectSocket(opts.timeout != null ? opts.timeout : 30000, socket, host, remoteAddress, localAddress, context); 470 | } 471 | }; 472 | } 473 | 474 | 475 | HttpMessageParserFactory responseParserFactory = new DefaultHttpResponseParserFactory() { 476 | @Override 477 | public HttpMessageParser create(SessionInputBuffer ibuffer, MessageConstraints constraints) { 478 | LineParser lineParser = new BasicLineParser() { 479 | 480 | @Override 481 | public Header parseHeader(final CharArrayBuffer buffer) { 482 | try { 483 | return super.parseHeader(buffer); 484 | } catch (ParseException ex) { 485 | return new BasicHeader(buffer.toString(), null); 486 | } 487 | } 488 | 489 | }; 490 | return new DefaultHttpResponseParser( 491 | ibuffer, lineParser, DefaultHttpResponseFactory.INSTANCE, constraints != null ? constraints : MessageConstraints.DEFAULT) { 492 | @Override 493 | protected boolean reject(final CharArrayBuffer line, int count) { 494 | //We need to break out of forever head reads 495 | if(count > 100) { 496 | return true; 497 | } 498 | return false; 499 | 500 | } 501 | 502 | }; 503 | } 504 | }; 505 | 506 | clientBuilder.setSSLSocketFactory(sslConnectionFactory); 507 | Registry registry = RegistryBuilder. create() 508 | .register("https", sslConnectionFactory) 509 | .register("http", PlainConnectionSocketFactory.INSTANCE) 510 | .build(); 511 | 512 | HttpMessageWriterFactory requestWriterFactory = new DefaultHttpRequestWriterFactory(); 513 | 514 | HttpConnectionFactory connFactory = new ManagedHttpClientConnectionFactory( 515 | requestWriterFactory, responseParserFactory); 516 | BasicHttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(registry, connFactory); 517 | clientBuilder.setConnectionManager(connectionManager); 518 | 519 | 520 | HttpClient client = clientBuilder.build(); 521 | httpClient = client; 522 | this.connectionManager = connectionManager; 523 | 524 | withClientFunction.method(client,cookieStore); 525 | } 526 | 527 | } 528 | 529 | public void shutdownClient() { 530 | if(connectionManager != null) { 531 | try { 532 | connectionManager.shutdown(); 533 | } catch(Exception ex) { 534 | log.error("Error Shutting Down Keep-Alive {}",ex.getMessage(),ex); 535 | } 536 | } 537 | } 538 | 539 | @FunctionalInterface 540 | public static interface WithClientFunction { 541 | void method(HttpClient client, BasicCookieStore cookieStore); 542 | } 543 | 544 | public static class RequestOptions { 545 | public Object body; 546 | public String contentType; //bodyType originally 547 | public Map headers; 548 | public Map queryParams; 549 | public Boolean suppressLog = true; 550 | public Boolean ignoreSSL=true; 551 | public Integer timeout = 30000; 552 | public Integer connectionTimeout = null; 553 | public Integer readTimeout = null; 554 | public Long contentLength = null; 555 | 556 | public OauthOptions oauth; 557 | public String apiToken; 558 | public HttpClient httpClient; //optional pass the client 559 | public HttpClientConnectionManager connectionManager; 560 | 561 | public static class OauthOptions { 562 | public String version; 563 | public String consumerKey; 564 | public String consumerSecret; 565 | public String apiKey; 566 | public String apiSecret; 567 | } 568 | } 569 | } 570 | 571 | -------------------------------------------------------------------------------- /src/main/java/com/bertramlabs/plugins/hcl4j/utils/ServiceResponse.java: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.LinkedHashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * ServiceResponse is a generic that allows you to strongly type models. 9 | * 10 | * Some scenarios are: 11 | * Respond with text/html of content. 12 | * Respond with data that will be serialized as json. 13 | * Respond with data that will be used in a template model. 14 | * 15 | * Response headers and cookies can also be set if required. 16 | * 17 | * @param The class that holds the data - must be serializable. Usually a Map or List. 18 | */ 19 | public class ServiceResponse { 20 | /** 21 | * The Key of key in the errors map that holds the errors. 22 | */ 23 | private static final String DEFAULT_ERROR_KEY = "error"; 24 | private Boolean success = false; 25 | private Boolean warning = false; 26 | private String msg = null; 27 | private Map errors = new LinkedHashMap<>(); 28 | private T data; 29 | private Map headers; 30 | private String content; 31 | private String errorCode; 32 | // Holds the parsed json map or array. 33 | // TODO: Add jackson or a java json lib. 34 | private Object results; 35 | private Map cookies; 36 | public Boolean inProgress = false; 37 | 38 | public ServiceResponse() { } 39 | 40 | 41 | public ServiceResponse(Boolean success, String msg, Map errors, T data) { 42 | this.success = success; 43 | this.msg = msg; 44 | if(errors != null) { 45 | this.errors = errors; 46 | } 47 | this.data = data; 48 | } 49 | 50 | /** 51 | * Helper to initialize a base response. 52 | * @return A generic respose assuming the repsonse is an error if not converted to a success response. 53 | */ 54 | public static ServiceResponse prepare() { 55 | return new ServiceResponse(false, null, null, null); 56 | } 57 | 58 | /** 59 | * Helper to initialize a base response with initial data. 60 | * @return A generic respose assuming the repsonse is an error if not converted to a success response. 61 | */ 62 | public static ServiceResponse prepare(Object data) { 63 | return new ServiceResponse(false, null, null, data); 64 | } 65 | 66 | /** 67 | * Helper to build an error response from a generic map. 68 | * @return A success or error response based on the boolean value of success in the map. 69 | */ 70 | public static ServiceResponse create(Map config) { 71 | Boolean configSuccess = config.get("success") == null ? false : (Boolean)config.get("success"); 72 | String configMsg = config.get("msg") != null ? (String)config.get("message") : null; 73 | Map configErrors = (LinkedHashMap)config.getOrDefault("errors", new LinkedHashMap()); 74 | Object configData = config.getOrDefault("data", new LinkedHashMap()); 75 | ServiceResponse rtn = new ServiceResponse(configSuccess, configMsg, configErrors, configData); 76 | Boolean inProgress = (Boolean)config.get("inProgress"); 77 | if(inProgress != null) { 78 | rtn.inProgress = inProgress; 79 | } 80 | Boolean warning = (Boolean)config.get("warning"); 81 | if(warning != null) { 82 | rtn.warning = warning; 83 | } 84 | return rtn; 85 | } 86 | 87 | /** 88 | * Helper to create service response from an existing service response. 89 | * Primarly a convenience method to prevent errors when a map has already been converted to a service response. 90 | * @return An unmodified service response. 91 | */ 92 | public static ServiceResponse create(ServiceResponse source) { 93 | return source; 94 | } 95 | 96 | /** 97 | * Helper to return a generic error response. 98 | * @return A generic error scenario. 99 | */ 100 | public static ServiceResponse error() { 101 | ServiceResponse serviceResponse = new ServiceResponse(false, null, null, null); 102 | serviceResponse.setError("error"); 103 | return serviceResponse; 104 | } 105 | 106 | /** 107 | * Helper to return a error message 108 | * @param msg Message to send to the user. 109 | * @return a ServiceResponse 110 | */ 111 | public static ServiceResponse error(String msg) { 112 | ServiceResponse serviceResponse = new ServiceResponse(false, null, null, null); 113 | serviceResponse.setError(msg); 114 | return serviceResponse; 115 | } 116 | 117 | /** 118 | * Detailed error message with a list of errors. 119 | * @param msg Message to send to the user. 120 | * @param errors Detailed list of errors 121 | * @return a ServiceResponse 122 | */ 123 | public static ServiceResponse error(String msg, Map errors) { 124 | return new ServiceResponse(false, msg, errors, null); 125 | } 126 | /** 127 | * Detailed error message with a list of errors. 128 | * @param msg Message to send to the user. 129 | * @param errors Detailed list of errors 130 | * @param data Any additional data needed for the view. 131 | * @return a ServiceResponse 132 | */ 133 | public static ServiceResponse error(String msg, Map errors, Object data) { 134 | return new ServiceResponse(false, msg, errors, data); 135 | } 136 | 137 | /** 138 | * Helper to return a success object with a message. 139 | * @param data object to pass back in success 140 | * @param msg success message 141 | * @return a ServiceResponse 142 | */ 143 | static ServiceResponse success(Object data, String msg) { 144 | return new ServiceResponse(true, msg, null, data); 145 | } 146 | 147 | /** 148 | * Helper to return a success message. 149 | * @param data object to pass back in success 150 | * @return a ServiceResponse 151 | */ 152 | public static ServiceResponse success(Object data) { 153 | return new ServiceResponse(true, null, null, data); 154 | } 155 | 156 | /** 157 | * Create a generic success response 158 | * @return success response 159 | */ 160 | public static ServiceResponse success() { 161 | return new ServiceResponse(true, null, null, null); 162 | } 163 | 164 | /** 165 | * Build a Map from this object with keys success, msg, errors, data 166 | * @return response Map 167 | */ 168 | public Map toMap() { 169 | return toMap(null); 170 | } 171 | 172 | /** 173 | * Serializes the ServiceResponse to a map. 174 | * @param dataKeyName the name to assign the data keys key in the map 175 | * @return A Map 176 | */ 177 | public Map toMap(String dataKeyName) { 178 | Map returnMap = new LinkedHashMap<>(); 179 | returnMap.put("success",success); 180 | returnMap.put("msg",msg); 181 | returnMap.put("errors",errors); 182 | returnMap.put( (dataKeyName != null ? dataKeyName : "data"),data); 183 | return returnMap; 184 | } 185 | 186 | /** 187 | * Return if the ServiceResponse has any errors set 188 | * @param key Check a specific key 189 | * @return boolean 190 | */ 191 | public boolean hasError(String key) { 192 | if(errors == null) return false; 193 | return errors.containsKey(key); 194 | } 195 | 196 | /** 197 | * Return if the ServiceResponse has any errors set 198 | * @return boolean 199 | */ 200 | public boolean hasErrors() { 201 | if(errors == null) return false; 202 | if(!success) return true; 203 | return errors.size() > 0; 204 | } 205 | 206 | /** 207 | * String representation of the toMap() method 208 | * @return the response as a String 209 | */ 210 | public String toString() { 211 | return toMap().toString(); 212 | } 213 | 214 | // Getters & Setters 215 | public Boolean getSuccess() { 216 | return this.success; 217 | } 218 | 219 | public void setSuccess(Boolean success) { 220 | this.success = success; 221 | } 222 | 223 | public String getMsg() { 224 | return msg; 225 | } 226 | 227 | public void setMsg(String msg) { 228 | this.msg = msg; 229 | } 230 | 231 | public T getData() { 232 | return data; 233 | } 234 | 235 | public void setData(T data) { 236 | this.data = data; 237 | } 238 | 239 | public Map getErrors() { 240 | return errors; 241 | } 242 | 243 | public void setErrors(Map errors) { 244 | if (errors != null) { 245 | this.success = false; 246 | } 247 | this.errors = errors; 248 | } 249 | 250 | public void addError(String value) { 251 | this.success = false; 252 | this.errors.put(DEFAULT_ERROR_KEY, value); 253 | } 254 | 255 | public void addError(String key, String value) { 256 | this.success = false; 257 | this.errors.put(key, value); 258 | } 259 | 260 | public void removeError() { 261 | this.errors.remove(DEFAULT_ERROR_KEY); 262 | if (this.errors.size() == 0) { 263 | this.success = true; 264 | } 265 | } 266 | 267 | public void clearErrors() { 268 | this.errors.clear(); 269 | this.success = true; 270 | } 271 | 272 | public void removeError(String key) { 273 | this.errors.remove(key); 274 | } 275 | 276 | /** 277 | * Returns the specific error message for a given key. 278 | * @param key that contains the error 279 | * @return The error value 280 | */ 281 | public String getError(String key) { 282 | return this.errors.getOrDefault(key, null); 283 | } 284 | 285 | /** 286 | * Provided for backwards compatibility with existing getError() 287 | * @return Error message 288 | */ 289 | public String getError() { 290 | return this.errors.getOrDefault(DEFAULT_ERROR_KEY, null); 291 | } 292 | 293 | /** 294 | * Provided for backwards compatibility with existing setError(msg) 295 | * @param value value to set 296 | */ 297 | public void setError(String value) { 298 | this.success = false; 299 | this.errors.put(DEFAULT_ERROR_KEY, value); 300 | } 301 | 302 | public Map getHeaders() { 303 | return headers; 304 | } 305 | 306 | public void setHeaders(Map headers) { 307 | this.headers = headers; 308 | } 309 | 310 | public void addHeader(String key, Object value) { 311 | if(this.headers == null) 312 | this.headers = new HashMap<>(); 313 | this.headers.put(key, value); 314 | } 315 | 316 | public String getContent() { 317 | return content; 318 | } 319 | 320 | public void setContent(String content) { 321 | this.content = content; 322 | } 323 | 324 | public String getErrorCode() { 325 | return errorCode; 326 | } 327 | 328 | public void setErrorCode(String errorCode) { 329 | this.errorCode = errorCode; 330 | } 331 | 332 | public Object getResults() { 333 | return results; 334 | } 335 | 336 | public void setResults(Object results) { 337 | this.results = results; 338 | } 339 | 340 | public Map getCookies() { 341 | return cookies; 342 | } 343 | 344 | public void setCookies(Map cookies) { 345 | this.cookies = cookies; 346 | } 347 | 348 | /** 349 | * Add a Cookie to the response 350 | * @param key cookie name 351 | * @param value cookie value 352 | */ 353 | public void addCookie(String key, Object value) { 354 | if(this.cookies == null) 355 | this.cookies = new HashMap<>(); 356 | this.cookies.put(key, value.toString()); 357 | } 358 | 359 | /** 360 | * Find a cookie 361 | * @param key cookie name 362 | * @return the cookie value 363 | */ 364 | public String getCookie(String key) { 365 | if(this.cookies == null) 366 | return null; 367 | return this.cookies.getOrDefault(key, null); 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /src/main/jflex/com/bertramlabs/plugins/hcl4j/HCLLexer.jflex: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j; 17 | import com.bertramlabs.plugins.hcl4j.symbols.*; 18 | import com.bertramlabs.plugins.hcl4j.RuntimeSymbols.*; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.io.*; 22 | 23 | /** 24 | * This class is a HCL lexer Generated from jflex. 25 | * @author David Estes 26 | */ 27 | 28 | %% 29 | 30 | %class HCLLexer 31 | %unicode 32 | %line 33 | %column 34 | %char 35 | %type Symbol 36 | 37 | %yylexthrow{ 38 | HCLParserException 39 | %yylexthrow} 40 | 41 | %{ 42 | StringBuffer string = new StringBuffer(); 43 | String endOfMultiLineSymbol; 44 | Boolean isMultiLineFirstNewLine = true; 45 | Boolean isMultilineModified = false; 46 | Boolean stringAttributeName = false; 47 | int curleyBraceCounter = 0; 48 | int interpolatedCurleyBraceCounter = 0; 49 | Symbol currentValue; 50 | String currentMapKey; 51 | public ArrayList elementStack = new ArrayList(); 52 | ArrayList blockNames = null; 53 | Boolean inMap = false; 54 | Boolean fromMapKey = false; 55 | HCLAttribute attribute; 56 | SubTypePrimitiveType subTypePrimitiveType; 57 | Integer primitiveDepth = 0; 58 | Integer previousState = null; 59 | 60 | Symbol currentBlock = null; 61 | private Symbol hclBlock(List blockNames) { 62 | //System.out.println("Starting Block"); 63 | HCLBlock block = new HCLBlock(blockNames,currentBlock,yyline,yycolumn-1,yychar-1); 64 | if(currentBlock == null) { 65 | elementStack.add(block); 66 | } else { 67 | currentBlock.appendChild(block); 68 | } 69 | currentBlock = block; 70 | return currentBlock; 71 | } 72 | 73 | private Symbol exitBlock() { 74 | 75 | Symbol result = null; 76 | if(currentBlock != null) { 77 | if(currentBlock.getParent() == null) { 78 | result = currentBlock; 79 | } 80 | currentBlock = currentBlock.getParent(); 81 | 82 | } 83 | return result; 84 | } 85 | 86 | private void startAttribute(String name) { 87 | //System.out.println("Starting Attribute"); 88 | 89 | HCLAttribute currentAttribute = new HCLAttribute(name,yyline,yycolumn,yychar); 90 | if(currentBlock == null) { 91 | elementStack.add(currentAttribute); 92 | } else { 93 | currentBlock.appendChild(currentAttribute); 94 | } 95 | currentBlock = currentAttribute; 96 | attribute = currentAttribute; 97 | } 98 | 99 | private void startAttribute(StringInterpolatedExpression expression) { 100 | //System.out.println("Starting Attribute"); 101 | 102 | HCLAttribute currentAttribute = new HCLAttribute(null,yyline,yycolumn,yychar); 103 | currentAttribute.runtimeName = expression; 104 | if(currentBlock == null) { 105 | elementStack.add(currentAttribute); 106 | } else { 107 | currentBlock.appendChild(currentAttribute); 108 | } 109 | currentBlock = currentAttribute; 110 | attribute = currentAttribute; 111 | } 112 | 113 | 114 | 115 | private void startInterpolatedString() { 116 | StringInterpolatedExpression currentAttribute; 117 | if(currentBlock != null && currentBlock instanceof StringInterpolatedExpression) { 118 | //we behave different since already in one 119 | currentAttribute = (StringInterpolatedExpression)currentBlock; 120 | } else { 121 | currentAttribute = new StringInterpolatedExpression(yyline,yycolumn,yychar); 122 | if(currentBlock == null) { 123 | elementStack.add(currentAttribute); 124 | } else { 125 | currentBlock.appendChild(currentAttribute); 126 | } 127 | } 128 | currentAttribute.appendChild(new HCLValue("string",string.toString(),yyline,yycolumn,yychar)); 129 | string.setLength(0); 130 | currentAttribute.appendChild(new Operator("+",yyline,yycolumn,yychar)); 131 | StringInterpolatedExpression expr = new StringInterpolatedExpression(yyline,yycolumn,yychar); 132 | currentAttribute.appendChild(expr); 133 | 134 | 135 | currentBlock = expr; 136 | yybegin(HCLATTRIBUTEVALUE); 137 | } 138 | 139 | 140 | 141 | private void startArray() { 142 | HCLArray currentAttribute = new HCLArray(yyline,yycolumn,yychar); 143 | if(currentBlock == null) { 144 | elementStack.add(currentAttribute); 145 | } else { 146 | currentBlock.appendChild(currentAttribute); 147 | } 148 | currentBlock = currentAttribute; 149 | yybegin(HCLARRAY); 150 | } 151 | 152 | 153 | private void startGroupedExpression() { 154 | GroupedExpression currentAttribute = new GroupedExpression(yyline,yycolumn,yychar); 155 | if(currentBlock == null) { 156 | elementStack.add(currentAttribute); 157 | } else { 158 | currentBlock.appendChild(currentAttribute); 159 | } 160 | currentBlock = currentAttribute; 161 | yybegin(HCLATTRIBUTEVALUE); 162 | } 163 | 164 | private void startNestedStringExpression() { 165 | StringInterpolatedExpression currentAttribute = new StringInterpolatedExpression(yyline,yycolumn,yychar); 166 | if(currentBlock == null) { 167 | elementStack.add(currentAttribute); 168 | } else { 169 | currentBlock.appendChild(currentAttribute); 170 | } 171 | currentBlock = currentAttribute; 172 | } 173 | 174 | 175 | private void startVariableTree() { 176 | VariableTree currentAttribute = new VariableTree(yyline,yycolumn,yychar); 177 | if(currentBlock == null) { 178 | elementStack.add(currentAttribute); 179 | } else { 180 | currentBlock.appendChild(currentAttribute); 181 | } 182 | currentBlock = currentAttribute; 183 | yypushback(yylength()); 184 | yybegin(VARIABLETREE); 185 | } 186 | 187 | 188 | private void startComputedTuple() { 189 | ComputedTuple currentAttribute = new ComputedTuple(yytext(),yyline,yycolumn,yychar); 190 | if(currentBlock == null) { 191 | elementStack.add(currentAttribute); 192 | } else { 193 | currentBlock.appendChild(currentAttribute); 194 | } 195 | currentBlock = currentAttribute; 196 | yybegin(FORTUPLEVARIABLES); 197 | } 198 | 199 | private void startForConditional() { 200 | ForConditional currentAttribute = new ForConditional(yyline,yycolumn,yychar); 201 | if(currentBlock == null) { 202 | elementStack.add(currentAttribute); 203 | } else { 204 | currentBlock.appendChild(currentAttribute); 205 | } 206 | currentBlock = currentAttribute; 207 | yybegin(HCLATTRIBUTEVALUE); 208 | } 209 | 210 | private void startComputedObject() { 211 | ComputedObject currentAttribute = new ComputedObject(yytext(),yyline,yycolumn,yychar); 212 | if(currentBlock == null) { 213 | elementStack.add(currentAttribute); 214 | } else { 215 | currentBlock.appendChild(currentAttribute); 216 | } 217 | currentBlock = currentAttribute; 218 | yybegin(FOROBJVARIABLES); 219 | } 220 | 221 | private void startFunction() { 222 | String functionName = yytext(); 223 | if(functionName.endsWith("(")) { 224 | functionName = functionName.substring(0,functionName.length() - 1); 225 | } 226 | Function currentAttribute = new Function(functionName,yyline,yycolumn,yychar); 227 | if(currentBlock == null) { 228 | elementStack.add(currentAttribute); 229 | } else { 230 | currentBlock.appendChild(currentAttribute); 231 | } 232 | currentBlock = currentAttribute; 233 | yybegin(FUNCTIONCALL); 234 | } 235 | 236 | 237 | private Symbol exitAttribute(Boolean force) { 238 | 239 | if(currentBlock == null) { 240 | yybegin(YYINITIAL); 241 | Symbol result = attribute; 242 | attribute = null; 243 | exitBlock(); 244 | return result; 245 | } else { 246 | attribute = null; 247 | if((!(currentBlock instanceof HCLArray) && !(currentBlock instanceof HCLMap) && !(currentBlock instanceof GroupedExpression) && !(currentBlock instanceof StringInterpolatedExpression) && !(currentBlock instanceof VariableTree) && !(currentBlock instanceof Variable) && !(currentBlock instanceof ComputedTuple) && !(currentBlock instanceof ComputedObject) && !(currentBlock instanceof Function)) || force == true) { 248 | exitBlock(); 249 | 250 | } 251 | 252 | if(currentBlock instanceof Variable || currentBlock instanceof VariableTree) { 253 | yybegin(VARIABLETREE); 254 | } else if(currentBlock instanceof SubTypePrimitiveType) { 255 | yybegin(SUBTYPEPRIMITIVETYPE); 256 | } else if(currentBlock instanceof StringInterpolatedExpression) { 257 | yybegin(HCLATTRIBUTEVALUE); 258 | } else if(currentBlock instanceof GroupedExpression) { 259 | yybegin(HCLATTRIBUTEVALUE); 260 | } else if(currentBlock instanceof ForConditional) { 261 | yybegin(HCLATTRIBUTEVALUE); 262 | } else if(currentBlock instanceof ComputedTuple) { 263 | yybegin(FORTUPLEEXPRESSION); 264 | } else if(currentBlock instanceof ComputedObject) { 265 | yybegin(FOROBJECTEXPRESSION); 266 | } else if(currentBlock instanceof Function) { 267 | yybegin(FUNCTIONCALL); 268 | } else if(currentBlock instanceof HCLBlock) { 269 | yybegin(HCLINBLOCK); 270 | } else if(currentBlock instanceof HCLArray) { 271 | yybegin(HCLARRAY); 272 | } else if(currentBlock instanceof HCLMap) { 273 | yybegin(HCLMAP); 274 | } else if(currentBlock instanceof HCLAttribute) { 275 | yybegin(HCLATTRIBUTEVALUE); 276 | } else { 277 | yybegin(YYINITIAL); 278 | } 279 | return null; 280 | } 281 | } 282 | 283 | private Symbol exitAttribute() { 284 | return exitAttribute(false); 285 | } 286 | 287 | private void startEvalExpression() { 288 | yypushback(yylength()); 289 | yybegin(EVALUATEDEXPRESSION); 290 | } 291 | 292 | 293 | 294 | %} 295 | 296 | LineTerminator = \r|\n|\r\n 297 | InputCharacter = [^\r\n] 298 | WhiteSpace = {LineTerminator} | [ \t\f] 299 | WhiteSpaceOpt = [ \t\f]+? 300 | WhiteSpaceSL = [ \t\f]+ 301 | WhiteSpaceNLOpt = [ \t\f\r\n]+? 302 | 303 | /* comments */ 304 | Comment = {TraditionalComment} | {EndOfLineComment} | {EndOfLineCommentHash} | {DocumentationComment} 305 | 306 | 307 | TraditionalComment = "/*" [^*]+ ~"*/" | "/*" "*"+ "/" 308 | // Comment can be the last line of the file, without line terminator. 309 | EndOfLineComment = "//" {InputCharacter}* 310 | EndOfLineCommentHash = "#" {InputCharacter}* 311 | DocumentationComment = "/**" {CommentContent} "*"+ "/" 312 | CommentContent = ( [^*] | \*+ [^/*] )* 313 | 314 | AnyChar = [^] 315 | 316 | ID_Start = [a-zA-Z\$_] 317 | Identifier = {ID_Start} [a-zA-Z0-9\-\_]* 318 | VariableBracketAccessor = \[ [a-zA-Z\"\'0-9\.\_\-\[\]\(\)]+ \] 319 | IdentifierTree = {ID_Start} [a-zA-Z0-9\-\_\.]* | {ID_Start} [a-zA-Z0-9\-\_\.]* {VariableBracketAccessor}+ 320 | 321 | GetAttr = "." {Identifier} 322 | Function = {ID_Start} [a-zA-Z0-9\-\_]*\( 323 | Arguments = ({Expression} ("," {Expression})* ("," | "...")?) 324 | FunctionCall = {Identifier} "(" 325 | 326 | ArrayModifier = {ID_Start} [a-zA-Z0-9\-\_]*\[ 327 | Property = {ID_Start} [a-zA-Z0-9\-\_]*\. 328 | EvaluatedExpression = [\(] | {IdentifierTree} | {ArrayModifier} | {Function} | {Identifier} 329 | 330 | True = true 331 | False = false 332 | Null = null 333 | 334 | StringPrimitive = string 335 | NumberPrimitive = number 336 | BooleanPrimitive = bool 337 | AnyPrimitive = any 338 | IfPrimitive = if 339 | ListPrimitive = list | list*\( | tuple | tuple*\( 340 | MapPrimitive = map | map*\( | object | object*\( 341 | SetPrimitive = set | set*\( 342 | ForInExpression = in 343 | 344 | DigitValue = [0-9\.\-]+ 345 | WholeNumber = [0-9]+ 346 | 347 | HCLAttributeName = {ID_Start} [a-zA-Z0-9\-\_\.]* 348 | HCLQuotedPropertyName = [\"] [^\r\n]+ [\"] 349 | 350 | HCLBlock = {HCLAttributeName} {HCLBlockAttribute}* "{" [^]* "}" | {HCLAttributeName} {WhiteSpaceOpt} "{" [^]* "}" 351 | 352 | HCLBlockAttribute = {WhiteSpaceOpt} "\"" {HCLDoubleStringCharacters} "\"" {WhiteSpaceOpt} | {WhiteSpace} "\'" {HCLSingleStringCharacters} "\'" {WhiteSpaceOpt} | {WhiteSpace} {HCLAttributeName} {WhiteSpaceOpt} 353 | 354 | HCLAttribute = {HCLAttributeName} {WhiteSpaceOpt} [=:] | {HCLQuotedPropertyName} {WhiteSpaceOpt} [=:] 355 | 356 | MapKeyDef = {MapKey} ":" 357 | MapKey = {HCLAttributeName} | "\"" {HCLDoubleStringCharacters} "\"" 358 | 359 | 360 | HCLDoubleStringCharacters = {HCLDoubleStringCharacter}* 361 | HCLSingleStringCharacters = {HCLSingleStringCharacter}* 362 | HCLDoubleStringCharacter = [^\r\n] 363 | HCLSingleStringCharacter = [^\'] 364 | EscapedInterpolation = [\$] [\$] 365 | InterpolationSyntax = [\$] "{" 366 | MLineModifierStart = [\<] [\<] [\-\~] {HCLAttributeName} 367 | MLineStart = [\<] [\<] [\ ]? {HCLAttributeName} 368 | 369 | 370 | ExprTerm = {True} | {False} | {Null} | {DigitValue} | {Identifier} | {FunctionCall} 371 | Conditional = \|\| | \&\& | \> [=]* | \< [=]* | == | \!= | [?] | \: | if 372 | Operation = [\+\-\/\*\%] | \! 373 | Expression = {ExprTerm} | {Operation} | {Conditional} 374 | /*For Expression*/ 375 | ForObjExpr = \{ [\n\t\f\r ]* {ForIntro} 376 | ForTupleExpr = \[ [\n\t\f\r ]* {ForIntro} 377 | ForExpr = {ForObjExpr} | {ForTupleExpr} 378 | 379 | 380 | ForIntro = "for" {WhiteSpaceSL} 381 | //ForExpr = {forTupleExpr} | {forObjectExpr}; 382 | //forTupleExpr = "[" {forIntro} {Expression} {forCond}? "]" 383 | //forObjectExpr = "{" {forIntro} {Expression} "=>" {Expression} "..."? {forCond}? "}" 384 | //forIntro = "for" {Identifier} ("," {Identifier})? "in" {Expression} ":" 385 | //forCond = "if" {Expression} 386 | 387 | /* Children */ 388 | 389 | JSXText = {JSXTextCharacter}+ 390 | JSXTextCharacter = [^\{\}\<\>] 391 | AssignmentExpression = [^] 392 | 393 | %state STRINGDOUBLE 394 | %state STRINGSINGLE 395 | %state HCLINBLOCK 396 | %state HCLBLOCKHEADER 397 | %state HCLBLOCKATTRIBUTES 398 | %state HCLATTRIBUTE 399 | %state HCLATTRIBUTEVALUE 400 | %state HCLARRAY 401 | %state HCLMAP 402 | %state HCLMAPKEY 403 | %state HCLMAPKEYDEF 404 | %state HCLMAPVALUE 405 | %state STRINGINTERPOLATED 406 | %state MULTILINESTRING 407 | %state EVALUATEDEXPRESSION 408 | %state FORLOOPEXPRESSION 409 | %state FORTUPLEEXPRESSION 410 | %state FOROBJVARIABLES 411 | %state FOROBJECTEXPRESSION 412 | %state FORTUPLEVARIABLES 413 | %state FORTUPLESOURCE 414 | %state FOROBJSOURCE 415 | %state FUNCTIONCALL 416 | %state SUBTYPEPRIMITIVETYPE 417 | %state VARIABLETREE 418 | 419 | %% 420 | 421 | /* keywords */ 422 | { 423 | /* identifiers */ 424 | {HCLBlock} {yybegin(HCLBLOCKHEADER);yypushback(yylength()); } 425 | {HCLAttribute} {yybegin(HCLATTRIBUTE);yypushback(yylength()); } 426 | /* comments */ 427 | {Comment} { /* ignore */ } 428 | 429 | /* whitespace */ 430 | {WhiteSpace} { /* ignore */ } 431 | {AnyChar} { /* ignore */ } 432 | } 433 | 434 | { 435 | \\t { string.append('\t'); } 436 | \\n { string.append('\n'); } 437 | \\r { string.append('\r'); } 438 | \\\\ { string.append('\\'); } 439 | } 440 | { 441 | 442 | \" { if(blockNames != null) { blockNames.add(string.toString()); yybegin(HCLBLOCKATTRIBUTES); } else if(currentBlock != null && currentBlock instanceof HCLMap && currentMapKey == null) { currentMapKey = string.toString() ; yybegin(HCLMAPKEYDEF); } else if (stringAttributeName) { stringAttributeName = false ; yybegin(HCLATTRIBUTE); if(currentBlock instanceof StringInterpolatedExpression) {StringInterpolatedExpression expr = (StringInterpolatedExpression)currentBlock; exitAttribute(true);startAttribute(expr);yybegin(HCLATTRIBUTE);} else { startAttribute(string.toString());}} else if(currentBlock != null) { currentBlock.appendChild(new HCLValue("string",string.toString(),yyline,yycolumn,yychar));if(currentBlock instanceof StringInterpolatedExpression){exitAttribute(true);} yybegin(HCLATTRIBUTEVALUE); } else { throw new HCLParserException("String block found outside of block or attribute assignment."); } } 443 | \\\" { string.append('\"'); } 444 | {EscapedInterpolation} { string.append( yytext() );} 445 | {InterpolationSyntax} { startInterpolatedString(); } 446 | \$[^\{\$\"] { string.append( yytext() ); } 447 | \$\" { string.append( "$" ); yypushback(yylength()-1); } 448 | [^\$\n\r\"\\]+ { string.append( yytext() ); } 449 | } 450 | 451 | { 452 | [^\n\r\'\\]+ { string.append( yytext() ); } 453 | \' { if(blockNames != null) { blockNames.add(string.toString()); yybegin(HCLBLOCKATTRIBUTES); } else if(currentBlock != null && currentBlock instanceof HCLMap && currentMapKey == null) { currentMapKey = string.toString() ; yybegin(HCLMAPKEYDEF); } else if (stringAttributeName) { stringAttributeName = false ; yybegin(HCLATTRIBUTE); startAttribute(string.toString());} else if(currentBlock != null) { currentBlock.appendChild(new HCLValue("string",string.toString(),yyline,yycolumn,yychar)); yybegin(HCLATTRIBUTEVALUE); } else { throw new HCLParserException("String block found outside of block or attribute assignment."); } } 454 | \\' { string.append('\''); } 455 | } 456 | 457 | { 458 | {LineTerminator} { if(isMultiLineFirstNewLine) {isMultiLineFirstNewLine = false; } else {string.append( yytext() );} } 459 | [^\n\r]+ { if(yytext().trim().equals(endOfMultiLineSymbol)) { endOfMultiLineSymbol = null; if(blockNames != null) { blockNames.add(string.toString()); yybegin(HCLBLOCKATTRIBUTES); } else if(attribute != null) { attribute.appendChild(new HCLValue("string",string.toString(),yyline,yycolumn,yychar)) ; exitAttribute(); } else { throw new HCLParserException("String block found outside of block or attribute assignment."); }} else {string.append( isMultilineModified ? yytext() : yytext() );} } 460 | } 461 | 462 | { 463 | \} { string.append(yytext()); if(interpolatedCurleyBraceCounter > 1) {interpolatedCurleyBraceCounter--;} else { interpolatedCurleyBraceCounter--; yybegin(STRINGDOUBLE);} } 464 | \{ { string.append(yytext()); interpolatedCurleyBraceCounter++; } 465 | \" {string.append(yytext());} 466 | [^\{\}\"\\]+ { string.append( yytext() ); } 467 | } 468 | 469 | { 470 | {HCLAttributeName} {yybegin(HCLBLOCKATTRIBUTES);blockNames = new ArrayList(); blockNames.add(yytext());} 471 | /* WhiteSpacespace */ 472 | {WhiteSpace} { /* ignore */ } 473 | } 474 | 475 | { 476 | \{ { curleyBraceCounter++ ; hclBlock(blockNames) ; blockNames = null ; yybegin(HCLINBLOCK); } 477 | \" {yybegin(STRINGDOUBLE); string.setLength(0);} 478 | {HCLAttributeName} { blockNames.add(yytext());} 479 | {WhiteSpace} { /* ignore */ } 480 | } 481 | 482 | { 483 | {HCLBlock} {yybegin(HCLBLOCKHEADER);yypushback(yylength()); } 484 | {HCLAttribute} {yybegin(HCLATTRIBUTE);yypushback(yylength()); } 485 | /* comments */ 486 | {Comment} { /* ignore */ } 487 | \} { exitAttribute();} 488 | /* whitespace */ 489 | {WhiteSpace} { /* ignore */ } 490 | } 491 | 492 | { 493 | \" {yybegin(STRINGDOUBLE); stringAttributeName = true ;string.setLength(0);} 494 | {HCLAttributeName} {startAttribute(yytext());} 495 | \= {yybegin(HCLATTRIBUTEVALUE); } 496 | \: {yybegin(HCLATTRIBUTEVALUE); } 497 | \, { /*ignore*/ } 498 | /* whitespace */ 499 | {WhiteSpace} { /* ignore */ } 500 | } 501 | 502 | 503 | 504 | 505 | { 506 | [^,\]\r\n\ \t] { yypushback(yylength()); yybegin(HCLATTRIBUTEVALUE); } 507 | \] { exitAttribute(true); } 508 | , { /* should probably process this but due to simplicity we dont need to */ } 509 | {Comment} { /* ignore */ } 510 | {WhiteSpace} { /* ignore */ } 511 | } 512 | 513 | 514 | { 515 | {ForExpr} { yybegin(FORLOOPEXPRESSION); yypushback(yylength()); } 516 | \[ { startArray();/* process an array */ } 517 | 518 | \{ { blockNames = new ArrayList(); blockNames.add(currentBlock.getName()); curleyBraceCounter++ ; hclBlock(blockNames) ; blockNames = null ; attribute = null ; yybegin(HCLINBLOCK); } 519 | \" {if(currentBlock instanceof StringInterpolatedExpression) {startNestedStringExpression();} yybegin(STRINGDOUBLE); string.setLength(0); } 520 | {MLineModifierStart} {yybegin(MULTILINESTRING) ; isMultiLineFirstNewLine = true ;isMultilineModified = true; string.setLength(0) ; endOfMultiLineSymbol = yytext().substring(3);} 521 | {MLineStart} {yybegin(MULTILINESTRING) ; isMultiLineFirstNewLine = true ;isMultilineModified = true; string.setLength(0) ; endOfMultiLineSymbol = yytext().substring(2).trim();} 522 | {True} { currentBlock.appendChild(new HCLValue("boolean","true",yyline,yycolumn,yychar)) ; } 523 | {False} { currentBlock.appendChild(new HCLValue("boolean","false",yyline,yycolumn,yychar)) ; } 524 | {Null} { currentBlock.appendChild(new HCLValue("null",null,yyline,yycolumn,yychar)) ; } 525 | {DigitValue} { currentBlock.appendChild(new HCLValue("number",yytext(),yyline,yycolumn,yychar)) ; } 526 | {StringPrimitive} { currentBlock.appendChild(new StringPrimitiveType(yyline,yycolumn,yychar)); } 527 | {NumberPrimitive} { currentBlock.appendChild(new NumberPrimitiveType(yyline,yycolumn,yychar)); } 528 | {BooleanPrimitive} { currentBlock.appendChild(new BooleanPrimitiveType(yyline,yycolumn,yychar)); } 529 | {AnyPrimitive} { currentBlock.appendChild(new AnyPrimitiveType(yyline,yycolumn,yychar)); } 530 | {ListPrimitive} { subTypePrimitiveType = new ListPrimitiveType(null,yyline,yycolumn,yychar); if(yytext().endsWith("(")) { yypushback(1);} currentBlock.appendChild(subTypePrimitiveType); yybegin(SUBTYPEPRIMITIVETYPE); } 531 | {SetPrimitive} { subTypePrimitiveType = new SetPrimitiveType(null,yyline,yycolumn,yychar); if(yytext().endsWith("(")) { yypushback(1);} currentBlock.appendChild(subTypePrimitiveType); yybegin(SUBTYPEPRIMITIVETYPE); } 532 | {MapPrimitive} { subTypePrimitiveType = new MapPrimitiveType(null,yyline,yycolumn,yychar); if(yytext().endsWith("(")) { yypushback(1);} currentBlock.appendChild(subTypePrimitiveType); yybegin(SUBTYPEPRIMITIVETYPE); } 533 | {Conditional} { currentBlock.appendChild(new Operator(yytext(),yyline,yycolumn,yychar)); } 534 | {Operation} { currentBlock.appendChild(new Operator(yytext(),yyline,yycolumn,yychar)); } 535 | {EvaluatedExpression} { startEvalExpression(); } 536 | {Comment} { /* ignore */ } 537 | {LineTerminator} { if(currentBlock instanceof HCLAttribute) {exitAttribute(true); } } 538 | \, { exitAttribute();} 539 | \] { if(currentBlock instanceof HCLArray || currentBlock instanceof ComputedTuple) {exitAttribute(true); } else if(currentBlock instanceof ForConditional) { exitAttribute(true); yypushback(yylength());} } 540 | {WhiteSpace} { /* ignore */ } 541 | \) { exitAttribute(true); } 542 | 543 | \} { if(currentBlock instanceof StringInterpolatedExpression) {exitAttribute(true);currentBlock.appendChild(new Operator("+",yyline,yycolumn,yychar));yybegin(STRINGDOUBLE);string.setLength(0);} else {yypushback(yylength()); exitAttribute(true); } } 544 | 545 | } 546 | 547 | { 548 | 549 | {FunctionCall} { startVariableTree(); } 550 | {IdentifierTree} {startVariableTree(); } 551 | \} { yypushback(yylength()); exitAttribute(true); } 552 | {LineTerminator} { if(currentBlock instanceof HCLAttribute) {exitAttribute(true); }} 553 | {Comment} { /* ignore */ } 554 | {WhiteSpace} { /* ignore */ } 555 | {Conditional} { currentBlock.appendChild(new Operator(yytext(),yyline,yycolumn,yychar)); } 556 | {Operation} { currentBlock.appendChild(new Operator(yytext(),yyline,yycolumn,yychar)); } 557 | \( { startGroupedExpression();/* ignore */ } 558 | \) { exitAttribute(true); } 559 | } 560 | 561 | { 562 | {FunctionCall} { startFunction(); } 563 | {Identifier} { currentBlock.appendChild(new Variable(yytext(),yyline,yycolumn,yychar)); } 564 | {WholeNumber} { startArray(); currentBlock.appendChild(new HCLValue("number",yytext(),yyline,yycolumn,yychar));exitAttribute(true); } 565 | \[ { startArray(); } 566 | \] { yypushback(yylength()); exitAttribute(true); } 567 | \( {startFunction(); } 568 | \) { yypushback(yylength()); exitAttribute(true);} 569 | \, { yypushback(yylength()); exitAttribute(true);} 570 | : { yypushback(yylength()); exitAttribute(true); } 571 | \? { yypushback(yylength()); exitAttribute(true); } 572 | \. { /*ignore*/ } 573 | {Conditional} { yypushback(yylength()); exitAttribute(true); } 574 | {Operation} { yypushback(yylength()); exitAttribute(true); } 575 | \} { yypushback(yylength()); exitAttribute(true); } 576 | {LineTerminator} { yypushback(yylength()); exitAttribute(true); } 577 | {Comment} { exitAttribute(true); } 578 | {WhiteSpace} { exitAttribute(true); } 579 | } 580 | 581 | { 582 | 583 | , {yybegin(HCLATTRIBUTEVALUE); } 584 | \) { exitAttribute(true); } 585 | {LineTerminator} { /* ignore */ } 586 | {Comment} { /* ignore */ } 587 | {WhiteSpace} { /* ignore */ } 588 | [^)\n\, ]+ { yypushback(yylength()); yybegin(HCLATTRIBUTEVALUE); } 589 | } 590 | 591 | { 592 | {ForObjExpr} { startComputedObject();} 593 | {ForTupleExpr} { startComputedTuple();} 594 | } 595 | 596 | { 597 | : { ((ComputedTuple)currentBlock).appendChild(new ForSource(yyline,yycolumn,yychar)) ; yybegin(HCLATTRIBUTEVALUE); } 598 | [\]] { exitAttribute(true); } 599 | {IfPrimitive} { startForConditional(); } 600 | {Conditional} { yybegin(HCLATTRIBUTEVALUE); yypushback(yylength()); } 601 | {Operation} { yybegin(HCLATTRIBUTEVALUE); yypushback(yylength()); } 602 | {Comment} { /* ignore */ } 603 | {WhiteSpace} { /* ignore */ } 604 | {LineTerminator} { /* ignore */ } 605 | 606 | } 607 | 608 | { 609 | {ForInExpression} { yybegin(HCLATTRIBUTEVALUE); } 610 | {Identifier} { if(yytext() == "in") {yybegin(HCLATTRIBUTEVALUE);} else {((ComputedTuple)currentBlock).getVariables().add(new Variable(yytext(),yyline,yycolumn,yychar));}} 611 | [,] {/*ignore*/} 612 | {Comment} { /* ignore */ } 613 | {WhiteSpace} { /* ignore */ } 614 | {LineTerminator} { /* ignore */ } 615 | } 616 | 617 | 618 | { 619 | {ForInExpression} { yybegin(HCLATTRIBUTEVALUE); } 620 | {Identifier} { if(yytext() == "in") {yybegin(HCLATTRIBUTEVALUE);} else {((ComputedObject)currentBlock).getVariables().add(new Variable(yytext(),yyline,yycolumn,yychar));}} 621 | [,] {/*ignore*/} 622 | {Comment} { /* ignore */ } 623 | {WhiteSpace} { /* ignore */ } 624 | {LineTerminator} { /* ignore */ } 625 | } 626 | 627 | 628 | { 629 | {EvaluatedExpression} { ((ComputedTuple)currentBlock).setSourceExpression( new Variable(yytext(),yyline,yycolumn,yychar));} 630 | : { yybegin(HCLATTRIBUTEVALUE); } 631 | 632 | {Comment} { /* ignore */ } 633 | {WhiteSpace} { /* ignore */ } 634 | {LineTerminator} { /* ignore */ } 635 | } 636 | 637 | { 638 | {EvaluatedExpression} { ((ComputedObject)currentBlock).setSourceExpression( new Variable(yytext(),yyline,yycolumn,yychar));} 639 | : { yybegin(HCLATTRIBUTEVALUE); } 640 | 641 | {Comment} { /* ignore */ } 642 | {WhiteSpace} { /* ignore */ } 643 | {LineTerminator} { /* ignore */ } 644 | } 645 | 646 | 647 | { 648 | : { yybegin(HCLATTRIBUTEVALUE); } 649 | \=\> { yybegin(HCLATTRIBUTEVALUE); } 650 | \} { exitAttribute(true); } 651 | {Comment} { /* ignore */ } 652 | {WhiteSpace} { /* ignore */ } 653 | {LineTerminator} { /* ignore */ } 654 | [^}\n]+ { /* ignore */ } 655 | } 656 | 657 | { 658 | \( { currentBlock = subTypePrimitiveType ; yybegin(HCLATTRIBUTEVALUE); } 659 | \) { exitAttribute(true); } 660 | {LineTerminator} { exitAttribute(true); } 661 | {Comment} { /* ignore */ } 662 | {WhiteSpace} { /* ignore */ } 663 | } 664 | 665 | 666 | 667 | /* error fallback */ 668 | [^] { throw new HCLParserException("Illegal character <("+ 669 | yytext()+ ") - state: " + yystate()+"> found on line: " + (yyline+1) + " col: " + (yycolumn+1) ); } 670 | -------------------------------------------------------------------------------- /src/test/groovy/com/bertramlabs/plugins/hcl4j/HCLBaseDataLookupsSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.bertramlabs.plugins.hcl4j 2 | 3 | import groovy.json.JsonOutput 4 | import spock.lang.Specification 5 | 6 | class HCLBaseDataLookupsSpec extends Specification { 7 | void "should process an http data lookup"() { 8 | given: 9 | 10 | def hcl = ''' 11 | data http "google" { 12 | url = "http://www.google.com" 13 | } 14 | 15 | 16 | resource "google1" { 17 | name = "google1" 18 | status = data.http.google.status_code 19 | body = data.http.google.response_body 20 | } 21 | resource "google2" { 22 | name = "google2" 23 | status = data.http.google.status_code 24 | body = data.http.google.response_body 25 | } 26 | 27 | ''' 28 | HCLParser parser = new HCLParser(); 29 | when: 30 | def results = parser.parse(hcl) 31 | then: 32 | results.resource.google1.status == 200 33 | results.resource.google1.body.contains("google") 34 | results.resource.google2.status == 200 35 | results.resource.google2.body.contains("google") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/groovy/com/bertramlabs/plugins/hcl4j/HCLLexerSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bertramlabs.plugins.hcl4j 17 | 18 | import com.bertramlabs.plugins.hcl4j.symbols.Symbol 19 | import spock.lang.Specification 20 | 21 | /** 22 | * @author David Estes 23 | */ 24 | class HCLLexerSpec extends Specification { 25 | 26 | void "should generate symbols from hcl"() { 27 | given: 28 | ArrayList rootBlocks = new ArrayList(); 29 | Symbol element; 30 | def hcl = ''' 31 | variables { 32 | test = "value" 33 | } 34 | 35 | service "myservice" { 36 | description = "test" 37 | info { 38 | name = "my name" 39 | maxMemory = 1024 40 | priority = 0.1 41 | enabled = true 42 | } 43 | } 44 | ''' 45 | StringReader reader = new StringReader(hcl); 46 | HCLLexer lexer = new HCLLexer(reader); 47 | when: 48 | lexer.yylex(); 49 | rootBlocks = lexer.elementStack 50 | 51 | println rootBlocks?.collect{[it.getSymbolName(),it.getName()]} 52 | then: 53 | rootBlocks.size() == 2 54 | } 55 | 56 | } 57 | --------------------------------------------------------------------------------