├── .circleci └── config.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTIONS.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme.md ├── settings.gradle └── src ├── main ├── assembly │ └── bin.xml ├── java │ └── com │ │ └── github │ │ └── havardh │ │ └── javaflow │ │ ├── Execution.java │ │ ├── JavaFlow.java │ │ ├── ast │ │ ├── Class.java │ │ ├── Enum.java │ │ ├── Field.java │ │ ├── List.java │ │ ├── Map.java │ │ ├── Method.java │ │ ├── Parent.java │ │ ├── Type.java │ │ └── builders │ │ │ ├── Builder.java │ │ │ ├── ClassBuilder.java │ │ │ ├── EnumBuilder.java │ │ │ ├── FieldBuilder.java │ │ │ ├── MethodBuilder.java │ │ │ └── TypeBuilder.java │ │ ├── exceptions │ │ ├── AggregatedException.java │ │ ├── ExitException.java │ │ ├── FieldGettersMismatchException.java │ │ └── MissingTypeException.java │ │ ├── model │ │ ├── CanonicalName.java │ │ ├── TypeMap.java │ │ └── builders │ │ │ └── CanonicalNameBuilder.java │ │ ├── phases │ │ ├── filetransform │ │ │ ├── CommentPrependTransformer.java │ │ │ ├── EslintDisableTransformer.java │ │ │ └── FileTransformer.java │ │ ├── parser │ │ │ ├── Parser.java │ │ │ └── java │ │ │ │ ├── CanonicalNameFactory.java │ │ │ │ ├── ClassVisitor.java │ │ │ │ ├── EnumVisitor.java │ │ │ │ ├── JavaParser.java │ │ │ │ ├── JavaParserFlags.java │ │ │ │ └── TypeFactory.java │ │ ├── reader │ │ │ └── FileReader.java │ │ ├── resolver │ │ │ └── FileResolver.java │ │ ├── transform │ │ │ ├── InheritanceTransformer.java │ │ │ ├── SortedTypeTransformer.java │ │ │ └── Transformer.java │ │ ├── verifier │ │ │ ├── ClassGetterNamingVerifier.java │ │ │ ├── MemberFieldsPresentVerifier.java │ │ │ └── Verifier.java │ │ └── writer │ │ │ ├── Writer.java │ │ │ └── flow │ │ │ ├── ClassWriter.java │ │ │ ├── EnumWriter.java │ │ │ ├── FieldDefinitionWriter.java │ │ │ ├── FlowWriter.java │ │ │ ├── TypeWriter.java │ │ │ └── converter │ │ │ ├── Converter.java │ │ │ ├── JavaFlowConverter.java │ │ │ └── definitions │ │ │ ├── Objects.java │ │ │ └── Primitives.java │ │ └── util │ │ ├── App.java │ │ ├── Lists.java │ │ └── Maps.java └── resources │ └── version.properties └── test ├── java └── com │ └── github │ └── havardh │ └── javaflow │ ├── ExecutionIntegrationTest.java │ ├── JavaFlowTest.java │ ├── exceptions │ ├── AggregatedExceptionTest.java │ ├── FieldGettersMismatchExceptionTest.java │ └── MissingTypeExceptionTest.java │ ├── model │ ├── Enumeration.java │ ├── Member.java │ ├── Model.java │ ├── ModelWithAnonymousClass.java │ ├── ModelWithCharArray.java │ ├── ModelWithCollection.java │ ├── ModelWithFieldComments.java │ ├── ModelWithGenericArrays.java │ ├── ModelWithGenericMapKey.java │ ├── ModelWithInnerClasses.java │ ├── ModelWithJavaLangObjects.java │ ├── ModelWithList.java │ ├── ModelWithMap.java │ ├── ModelWithMapGenericValue.java │ ├── ModelWithNotMatchingBooleanGetter.java │ ├── ModelWithNotMatchingGetter.java │ ├── ModelWithNotMatchingGetterType.java │ ├── ModelWithNullableField.java │ ├── ModelWithPrimitive.java │ ├── ModelWithSet.java │ ├── ModelWithStaticFields.java │ ├── ModelWithWildcardTypeParams.java │ ├── ModelWithoutGetters.java │ ├── Sub.java │ ├── Super.java │ ├── Top.java │ ├── Wrapper.java │ ├── fixtures │ │ ├── CanonicalNameFixtures.java │ │ ├── FieldDefinitionFixtures.java │ │ └── TypeFixtures.java │ └── packaged │ │ └── PackagedMember.java │ ├── phases │ ├── filetransform │ │ ├── CommentPrependTransformerTest.java │ │ └── EslintDisableTransformerTest.java │ ├── parser │ │ └── java │ │ │ ├── CanonicalNameFactoryTest.java │ │ │ └── TypeFactoryTest.java │ ├── resolver │ │ └── FileResolverTest.java │ ├── transform │ │ ├── InheritanceTransformerTest.java │ │ └── SortedTypeTransformerTest.java │ ├── verifier │ │ ├── ClassGetterNamingVerifierTest.java │ │ └── MemberFieldsPresentVerifierTest.java │ └── writer │ │ ├── WriterTest.java │ │ └── flow │ │ ├── ClassWriterTest.java │ │ ├── EnumWriterTest.java │ │ ├── FieldWriterTest.java │ │ ├── TypeWriterTest.java │ │ └── converter │ │ └── JavaFlowConverterTest.java │ └── testutil │ ├── Assertions.java │ ├── MapConverter.java │ └── MapConverterFixture.java └── resources └── log4j2-test.xml /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/openjdk:8-jdk 6 | working_directory: ~/repo 7 | steps: 8 | - checkout 9 | - restore_cache: 10 | keys: 11 | - v1-dependencies-{{ checksum "build.gradle" }} 12 | - v1-dependencies- 13 | - run: ./gradlew dependencies -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD 14 | - run: ./gradlew test 15 | - save_cache: 16 | paths: 17 | - ~/.m2 18 | key: v1-dependencies-{{ checksum "build.gradle" }} 19 | - run: ./gradlew assemble -x signArchives -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *.iml 3 | .idea 4 | scripts 5 | build 6 | out 7 | .gradle 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at havardwhoiby@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /CONTRIBUTIONS.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for showing an interest in contributing to javaflow! 4 | 5 | **Working on your first Pull Request?** You can learn how from this _free_ 6 | series [How to Contribute to an Open Source Project on GitHub][egghead] by 7 | Kent C. Dodds. 8 | 9 | By making a contribution to this project you agree to abide by the 10 | [Code of Conduct][code-of-conduct]. 11 | 12 | ## Project setup 13 | 14 | This project uses a bundled `gradle`package manager. 15 | 16 | 1. Fork and clone the repo 17 | 2. `./gradlew build` - builds sources 18 | 3. `git checkout -b ` Create a branch for your PR 19 | 20 | ### Tests and static analysis 21 | 22 | Tests are mainly added as integration tests in [`ExecutionIntegrationTest`]([integration-tests]). 23 | 24 | - `./gradlew test` - Runs all the tests 25 | 26 | #### Continuous integration 27 | 28 | Circle CI is used for continuous integration. 29 | 30 | ## Conventions 31 | 32 | The [angular commit guide][angular-commit] line is used to govern commit messages. 33 | If possible do not force push changes to branches with open pull requests, unless 34 | it is done to rebase on the master branch. This will make code reviews easier. 35 | 36 | ## Help needed 37 | 38 | Please checkout the [the open issues][issues] 39 | 40 | Also, please watch the repo and respond to questions/bug reports/feature 41 | requests! Thanks! 42 | 43 | ## Attribution 44 | 45 | This guide is adapted from the [Downshift][downshift] contributors guide. 46 | 47 | [egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github 48 | [issues]: https://github.com/havardh/javaflow/issues 49 | [downshift]: https://github.com/paypal/downshift 50 | [flowtype]: https://flow.org/ 51 | [angular-commit]: https://gist.github.com/stephenparish/9941e89d80e2bc58a153 52 | [code-of-conduct]: CODE_OF_CONDUCT.md 53 | [integration-tests]: test/java/com/github/havardh/javaflow/ExecutionIntegrationTest.java 54 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'idea' 3 | apply plugin: 'application' 4 | apply plugin: 'org.junit.platform.gradle.plugin' 5 | apply plugin: 'maven' 6 | apply plugin: 'maven-publish' 7 | apply plugin: 'signing' 8 | 9 | group = 'com.github.havardh' 10 | version = '1.5.1-SNAPSHOT' 11 | mainClassName = "com.github.havardh.javaflow.JavaFlow" 12 | 13 | description = """""" 14 | 15 | sourceCompatibility = 1.8 16 | targetCompatibility = 1.8 17 | 18 | repositories { 19 | maven { url "http://repo.maven.apache.org/maven2" } 20 | } 21 | 22 | buildscript { 23 | repositories { 24 | mavenCentral() 25 | } 26 | dependencies { 27 | classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2' 28 | } 29 | } 30 | 31 | junitPlatform { 32 | platformVersion '1.0.2' 33 | // enableStandardTestTask true 34 | // reportsDir "build/test-results/junit-platform" // this is the default 35 | logManager 'org.apache.logging.log4j.jul.LogManager' 36 | } 37 | 38 | publishing { 39 | publications { 40 | mavenJava(MavenPublication) { 41 | from components.java 42 | } 43 | } 44 | } 45 | 46 | task writeNewPom { 47 | doLast { 48 | pom { 49 | project { 50 | inceptionYear '2017' 51 | } 52 | }.writeTo("$buildDir/pom.xml") 53 | } 54 | } 55 | 56 | task javadocJar(type: Jar, dependsOn: javadoc) { 57 | classifier = 'javadoc' 58 | from javadoc 59 | } 60 | 61 | task sourcesJar(type: Jar, dependsOn: classes) { 62 | classifier = 'sources' 63 | from sourceSets.main.allSource 64 | } 65 | 66 | task createProperties(dependsOn: processResources) { 67 | doLast { 68 | new File("$buildDir/resources/main/version.properties").withWriter { w -> 69 | Properties p = new Properties() 70 | p['version'] = project.version.toString() 71 | p.store w, null 72 | } 73 | } 74 | } 75 | 76 | classes { 77 | dependsOn createProperties 78 | } 79 | 80 | artifacts { 81 | archives javadocJar, sourcesJar 82 | } 83 | 84 | signing { 85 | required { gradle.taskGraph.hasTask("uploadArchives") } 86 | sign configurations.archives 87 | } 88 | 89 | uploadArchives { 90 | repositories { 91 | mavenDeployer { 92 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 93 | 94 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 95 | authentication(userName: ossrhUsername, password: ossrhPassword) 96 | } 97 | 98 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 99 | authentication(userName: ossrhUsername, password: ossrhPassword) 100 | } 101 | 102 | pom.project { 103 | name 'javaflow' 104 | packaging 'jar' 105 | // optionally artifactId can be defined here 106 | description 'Java model to flowtype converter' 107 | url 'https://github.com/havardh/javaflow' 108 | 109 | scm { 110 | connection 'scm:git:git://github.com/havardh/javaflow' 111 | developerConnection 'scm:git:ssh://github.com:havardh/javaflow.git' 112 | url 'https://github.com/havardh/javaflow/tree/master' 113 | } 114 | 115 | licenses { 116 | license { 117 | name 'MIT License' 118 | url 'http://www.opensource.org/licenses/mit-license.php' 119 | } 120 | } 121 | 122 | developers { 123 | developer { 124 | id 'havardh' 125 | name 'Håvard Wormdal Høiby' 126 | email 'havardwhoiby@gmail.com' 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | 134 | dependencies { 135 | compile group: 'com.github.javaparser', name: 'javaparser-core', version:'3.7.0' 136 | compile group: 'com.esotericsoftware.yamlbeans', name: 'yamlbeans', version:'1.08' 137 | compile 'info.picocli:picocli:3.8.0' 138 | testRuntime group: 'org.apache.logging.log4j', name: 'log4j-jul', version: '2.7' 139 | testRuntime group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.7' 140 | testRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.0.2' 141 | testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version:'5.0.2' 142 | testCompile group: 'org.hamcrest', name: 'hamcrest-all', version:'1.3' 143 | testCompile group: 'com.google.code.findbugs', name: 'annotations', version:'2.0.1' 144 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ossrhUsername=null 2 | ossrhPassword=null 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/havardh/javaflow/392188a5d28124e6e8df924bb7c58d0eff99108b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Dec 02 11:40:58 CET 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 165 | if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then 166 | cd "$(dirname "$0")" 167 | fi 168 | 169 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 170 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Javaflow # 2 | 3 | `javaflow` converts a set of Java models to Flow types. 4 | [![CircleCI](https://circleci.com/gh/havardh/javaflow.svg?style=svg)](https://circleci.com/gh/havardh/javaflow) 5 | 6 | ## Example ## 7 | 8 | `javaflow` takes a list of Java class files, and writes the flow types 9 | for the models to the standard output. 10 | 11 | A simple example is the following conversion of `Post.java` to `post.js`. 12 | 13 | ``` 14 | public class Post { 15 | private String title; 16 | private String text; 17 | 18 | public String getTitle() { return this.title; } 19 | public String getText() { return this.text; } 20 | } 21 | ``` 22 | 23 | ``` 24 | javaflow Post.java >post.js 25 | ``` 26 | 27 | ``` 28 | /* @flow */ 29 | export type Post { 30 | title: string, 31 | text: string, 32 | }; 33 | ``` 34 | 35 | ## Installation ## 36 | 37 | ### Embedding in maven build 38 | 39 | See the [readme](https://github.com/havardh/javaflow-maven-plugin/blob/master/Readme.md) of the javaflow-maven-plugin repository. 40 | 41 | ### Standalone cli tool 42 | 43 | Download the most recent zip file under releases. 44 | Unzip the downloaded file to the desired location like `~/apps/javaflow`. 45 | Add `~/app/javaflow/bin` to your `PATH` variable. 46 | 47 | ## Advanced usage ## 48 | 49 | ### Custom type substitution ### 50 | 51 | Internally `javaflow` contains a mapping from standard Java types like String, int and boolean 52 | to corresponding flow types. This mapping can be overridden, or additional mappings can be provided. 53 | 54 | An use case for providing custom type substitutions is when the Java model contains a type with 55 | a custom serialized form. 56 | 57 | Consider the model `Person` with the `Ssn` wrapper type below. 58 | 59 | ``` 60 | com.github.havardh.examples; 61 | 62 | public class Ssn { 63 | private String ssn; 64 | 65 | public String getSsn() { return this.ssn; } 66 | } 67 | ``` 68 | 69 | ``` 70 | com.github.havardh.examples; 71 | 72 | public class Person { 73 | private Ssn ssn; 74 | 75 | public Ssn getSsn() { return this.ssn; } 76 | } 77 | ``` 78 | 79 | Lets say we the Java api layer contains a custom json serializer which removes the wrapper 80 | type and outputs the ssn directly like so: 81 | 82 | ``` 83 | {"ssn": "12120032701"} 84 | ``` 85 | 86 | Without any further configuration, `javaflow Person.java Ssn.java` would convert the types as follows 87 | 88 | ``` 89 | /* @flow */ 90 | export type Ssn = { 91 | ssn: string, 92 | }; 93 | 94 | export type Person = { 95 | ssn: Ssn, 96 | }; 97 | ``` 98 | 99 | Here we want the string to be included in the Person type directly. 100 | This can be achieved by supplying a `types.yml`. The `types.yml` file is 101 | read from the folder the `javaflow` command is executed from. 102 | 103 | ``` 104 | com.github.havardh.examples.Ssn: string 105 | ``` 106 | 107 | The format of the yaml file is `: `. 108 | 109 | Now `javaflow Person.java` will output: 110 | 111 | ``` 112 | /* @flow */ 113 | export type Person = { 114 | ssn: string, 115 | }; 116 | ``` 117 | 118 | Note: executing the command `javaflow Person.java` without the `types.yml` file would lead 119 | to the program failing due to the `Ssn.java` file not being found. `javaflow` does not look 120 | for additional files which where not supplied as input. 121 | 122 | ### Verifiers ### 123 | 124 | In case you want to verify that you have specified mappings for all types, 125 | that the DTOs have getters for all fields, 126 | or that the type of the fields are same as the return types of their getters, 127 | `javaflow` offers the possibility to add verifiers. 128 | 129 | Available verifiers: 130 | - `MemberFieldsPresentVerifier`, active by default, verifies that all types used in the types converted are either: 131 | - built-in java types, 132 | - overridden by custom type substitution, or 133 | - included as a type being converted. 134 | - `ClassGetterNamingVerifier`, can be activated by passing the `--verifyGetters` flag in the CLI, verifies that the given types all have: 135 | - the same number of fields and getters, 136 | - the same type in the field definitions as the return type in the corresponding getter and 137 | - their getters for the corresponding fields with the same name, prefixed with `get` or `is`. 138 | 139 | ### Annotations ### 140 | 141 | - A field annotated with a `Nullable` annotation will translated to a Maybe type. 142 | 143 | ## Development ## 144 | 145 | ### Package ### 146 | 147 | Run the following command to package a zip file with all dependencies and the `javaflow` binary. 148 | 149 | `gradle assemble` 150 | 151 | This create a zip file in the target folder. 152 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'javaflow' 2 | -------------------------------------------------------------------------------- /src/main/assembly/bin.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | zip 8 | 9 | 10 | 11 | 12 | 13 | target/appassembler/lib 14 | lib 15 | 16 | 17 | target/appassembler/bin 18 | bin 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/Execution.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow; 2 | 3 | import com.github.havardh.javaflow.ast.Type; 4 | import com.github.havardh.javaflow.exceptions.AggregatedException; 5 | import com.github.havardh.javaflow.exceptions.ExitException; 6 | import com.github.havardh.javaflow.exceptions.ExitException.ErrorCode; 7 | import com.github.havardh.javaflow.phases.filetransform.FileTransformer; 8 | import com.github.havardh.javaflow.phases.parser.Parser; 9 | import com.github.havardh.javaflow.phases.reader.FileReader; 10 | import com.github.havardh.javaflow.phases.resolver.FileResolver; 11 | import com.github.havardh.javaflow.phases.transform.Transformer; 12 | import com.github.havardh.javaflow.phases.verifier.Verifier; 13 | import com.github.havardh.javaflow.phases.writer.Writer; 14 | 15 | import java.io.IOException; 16 | import java.io.StringWriter; 17 | import java.nio.file.Path; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Optional; 21 | import java.util.stream.Collectors; 22 | import java.util.stream.Stream; 23 | 24 | import static java.util.Arrays.asList; 25 | import static java.util.stream.Collectors.toList; 26 | 27 | /** 28 | * JavaFlow Execution runner 29 | * 30 | * The Execution object is set up with the readers, parsers, 31 | * transformers, verifiers, writers and file transformers. 32 | * Then the {@code run} method will run the execution for 33 | * the given setup. 34 | */ 35 | public class Execution { 36 | 37 | private final FileResolver resolver; 38 | private final FileReader reader; 39 | private final Parser parser; 40 | private final List transformers; 41 | private final List verifiers; 42 | private final Writer writer; 43 | private final List fileTransformers; 44 | 45 | /** 46 | * Sets up an Execution with the given set of configurations. 47 | * 48 | * @param resolver - file resolver 49 | * @param reader - file reader 50 | * @param parser - code parser 51 | * @param transformers - JavaFlow transforms 52 | * @param verifiers - JavaFlow verifiers 53 | * @param writer - string writer 54 | * @param fileTransformers - output file transforms 55 | */ 56 | public Execution( 57 | FileResolver resolver, 58 | FileReader reader, 59 | Parser parser, 60 | List transformers, 61 | List verifiers, 62 | Writer writer, 63 | List fileTransformers 64 | ) { 65 | this.resolver = resolver; 66 | this.reader = reader; 67 | this.parser = parser; 68 | this.transformers = transformers; 69 | this.verifiers = verifiers; 70 | this.writer = writer; 71 | this.fileTransformers = fileTransformers; 72 | } 73 | 74 | /** 75 | * Run the execution on the given set of files. 76 | * 77 | * This is the heart of the JavaFlow program. This method will resolve 78 | * all matching files with the {@code resolver}, read 79 | * each file with the file {@code reader}, parse the source code with 80 | * the {@code parser}. Next it will run all the {@code transforms} and 81 | * {@code verifiers}, before it writes the results to a {@code String} 82 | * with the {@code writer}. This string is transformed by the 83 | * {@code fileTransformers} before the result is returned to the caller. 84 | * 85 | * @param patterns list of file patterns to create flow types for. 86 | * @return the flow type as a {@code String} 87 | */ 88 | public String run(String... patterns) { 89 | 90 | return Stream.of(asList(patterns)) 91 | .map(this::resolve) 92 | .map(this::read) 93 | .map(this::parse) 94 | .peek(this::transform) 95 | .peek(this::verify) 96 | .map(this::write) 97 | .map(this::transformFile) 98 | .collect(Collectors.joining()); 99 | } 100 | 101 | /** 102 | * Runs all {@code verifiers} on the list of {$code types} 103 | * 104 | * @param types list of JavaFlow internal type 105 | * @throws com.github.havardh.javaflow.exceptions.MissingTypeException when 106 | * a type is references which is not in the given set of types or overrides 107 | */ 108 | private void verify(List types) { 109 | List exceptions = new ArrayList<>(); 110 | for (Verifier verifier : verifiers) { 111 | try { 112 | verifier.verify(types); 113 | } catch (Exception e) { 114 | exceptions.add(e); 115 | } 116 | } 117 | 118 | if (!exceptions.isEmpty()) { 119 | throw new AggregatedException("Verification failed:\n", exceptions, false); 120 | } 121 | } 122 | 123 | /** 124 | * Resolves all files matching the pattern. 125 | * 126 | * @param pattern a file, directory or glob pattern 127 | * @return list of files matching the pattern 128 | */ 129 | private List resolve(List pattern) { 130 | return pattern.stream() 131 | .map(resolver::resolve) 132 | .flatMap(List::stream) 133 | .collect(Collectors.toList()); 134 | } 135 | 136 | /** 137 | * Read each {@code file} into a list of {@code String} with the {@code reader}. 138 | * 139 | * @param filenames list of files to read 140 | * @return list of file content 141 | */ 142 | private List read(List filenames) { 143 | return filenames.stream() 144 | .map(path -> path.toFile().getPath()) 145 | .map(reader::read) 146 | .filter(Optional::isPresent) 147 | .map(Optional::get) 148 | .collect(Collectors.toList()); 149 | } 150 | 151 | /** 152 | * Parse each {@code source} to a list of {@code Type}. The {@code Type} is the 153 | * internal representation of a model. 154 | * 155 | * @param sources list of source code 156 | * @return list of internal types 157 | */ 158 | private List parse(List sources) { 159 | return sources.stream() 160 | .flatMap(source -> parser.parse(source).stream()) 161 | .collect(toList()); 162 | } 163 | /** 164 | * Transforms each {@code type} with the set of {@code transformers} 165 | * 166 | * @param types list of types 167 | */ 168 | private void transform(List types) { 169 | transformers.forEach(transformer -> transformer.transform(types)); 170 | } 171 | 172 | /** 173 | * Writes the list of {@code Type} into a {@code String} 174 | * 175 | * @param types list of types 176 | * @return the flow types as a {@code String} 177 | */ 178 | private String write(List types) { 179 | StringWriter stringWriter = new StringWriter(); 180 | 181 | for (int i = 0; i < types.size(); i++) { 182 | if (i != 0) { 183 | stringWriter.write("\n"); 184 | } 185 | try { 186 | writer.write(types.get(i), stringWriter); 187 | } catch (IOException e) { 188 | throw new ExitException(ErrorCode.COULD_NOT_WRITE_OUTPUT, e); 189 | } 190 | if (i != types.size() - 1) { 191 | stringWriter.write("\n"); 192 | } 193 | } 194 | 195 | return stringWriter.toString(); 196 | } 197 | 198 | /** 199 | * Transforms a output file with the set of {@code fileTransformers} 200 | * 201 | * @param inputFile the output file 202 | * @return transformed output file 203 | */ 204 | private String transformFile(String inputFile) { 205 | String file = inputFile; 206 | 207 | for (FileTransformer transformer : fileTransformers) { 208 | file = transformer.transform(file); 209 | } 210 | 211 | return file; 212 | } 213 | } 214 | 215 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/JavaFlow.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow; 2 | 3 | import static com.github.havardh.javaflow.phases.parser.java.JavaParserFlags.flagsBuilder; 4 | import static java.lang.String.format; 5 | import static java.util.Arrays.asList; 6 | import static java.util.Collections.singletonList; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import com.github.havardh.javaflow.exceptions.ExitException; 12 | import com.github.havardh.javaflow.model.TypeMap; 13 | import com.github.havardh.javaflow.phases.filetransform.CommentPrependTransformer; 14 | import com.github.havardh.javaflow.phases.filetransform.EslintDisableTransformer; 15 | import com.github.havardh.javaflow.phases.parser.java.JavaParser; 16 | import com.github.havardh.javaflow.phases.reader.FileReader; 17 | import com.github.havardh.javaflow.phases.resolver.FileResolver; 18 | import com.github.havardh.javaflow.phases.transform.InheritanceTransformer; 19 | import com.github.havardh.javaflow.phases.transform.SortedTypeTransformer; 20 | import com.github.havardh.javaflow.phases.verifier.ClassGetterNamingVerifier; 21 | import com.github.havardh.javaflow.phases.verifier.MemberFieldsPresentVerifier; 22 | import com.github.havardh.javaflow.phases.verifier.Verifier; 23 | import com.github.havardh.javaflow.phases.writer.flow.FlowWriter; 24 | import com.github.havardh.javaflow.phases.writer.flow.converter.Converter; 25 | import com.github.havardh.javaflow.phases.writer.flow.converter.JavaFlowConverter; 26 | import com.github.havardh.javaflow.util.App; 27 | import picocli.CommandLine; 28 | 29 | /** 30 | * Commmand line runner for JavaFlow 31 | */ 32 | @CommandLine.Command( 33 | name = "javaflow", 34 | description = "convert java models to flow types", 35 | footer = "Open source MIT --- https://www.github.com/havardh/javaflow" 36 | ) 37 | public class JavaFlow implements Runnable { 38 | 39 | @CommandLine.Option(names = { "--verifyGetters" }, description = "Verify that field and getter names match") 40 | private boolean verifyGetters; 41 | 42 | @CommandLine.Option(names = { "--includeStaticFields"}, description = "Include static fields", defaultValue = "false") 43 | private boolean includeStaticFields; 44 | 45 | @CommandLine.Parameters(arity = "1..*", paramLabel = "file|folder|glob", description = "File(s), directorie(s) or glob patterns to process") 46 | private String[] patterns; 47 | 48 | /** 49 | * Main routine for JavaFlow command line runner 50 | * 51 | * @param args command line arguments 52 | */ 53 | public static void main(String args[]) { 54 | CommandLine.run(new JavaFlow(), args); 55 | } 56 | 57 | /** 58 | * Execute the javaflow progam with command line arguments 59 | * 60 | * Runnable.run method executed by picocli.Commandline.run 61 | */ 62 | public void run() { 63 | TypeMap typeMap = new TypeMap("types.yml"); 64 | Converter converter = new JavaFlowConverter(typeMap); 65 | 66 | List verifierList = new ArrayList<>(); 67 | verifierList.add(new MemberFieldsPresentVerifier(typeMap)); 68 | if (verifyGetters) { 69 | verifierList.add(new ClassGetterNamingVerifier()); 70 | } 71 | 72 | Execution execution = new Execution( 73 | new FileResolver(), 74 | new FileReader(), 75 | new JavaParser(flagsBuilder().includeStaticFields(includeStaticFields).build()), 76 | asList( 77 | new InheritanceTransformer(), 78 | new SortedTypeTransformer() 79 | ), 80 | verifierList, 81 | new FlowWriter(converter), 82 | asList( 83 | new CommentPrependTransformer(format("Generated by javaflow %s", App.version())), 84 | new EslintDisableTransformer(singletonList("no-use-before-define")), 85 | new CommentPrependTransformer("@flow") 86 | ) 87 | ); 88 | 89 | try { 90 | System.out.println(execution.run(patterns)); 91 | } catch (ExitException e) { 92 | e.printStackTrace(); 93 | System.exit(e.getErrorCode().getCode()); 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/Class.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast; 2 | 3 | import static java.util.Collections.emptyList; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | import com.github.havardh.javaflow.model.CanonicalName; 9 | import com.github.havardh.javaflow.util.Lists; 10 | 11 | /** 12 | * Internal representation of a class 13 | */ 14 | public class Class extends Type { 15 | 16 | private final Parent parent; 17 | private final List innerClasses; 18 | private final List fields; 19 | private final List getters; 20 | 21 | /** 22 | * Create a {@code Class} with the given name, optional parent and 23 | * list of member fields. 24 | * 25 | * @param name the name of the class 26 | * @param parent optional parent of the type 27 | * @param innerClasses the list of inner classes 28 | * @param fields the list of member fields 29 | * @param getters the list of getters in the class 30 | */ 31 | public Class(CanonicalName name, Parent parent, List innerClasses, List fields, List getters) { 32 | super(name); 33 | this.parent = parent; 34 | this.innerClasses = innerClasses; 35 | this.fields = fields; 36 | this.getters = getters; 37 | } 38 | 39 | /** 40 | * Get list of {@code Class} which are inner classes 41 | * @return list of inner classes 42 | */ 43 | public List getInnerClasses() { 44 | return innerClasses; 45 | } 46 | 47 | /** 48 | * Get the list of fields including the fields of all parents 49 | * 50 | * @return the list of all fields for the type 51 | */ 52 | public List getFields() { 53 | return Lists.concat(getParentFields(), fields); 54 | } 55 | 56 | private List getParentFields() { 57 | return getParent().map(Parent::getFields).orElse(emptyList()); 58 | } 59 | 60 | private List getParentGetters() { 61 | return getParent().map(Parent::getGetters).orElse(emptyList()); 62 | } 63 | 64 | /** 65 | * Get the {@code Parent} reference 66 | * 67 | * @return optional parent reference 68 | */ 69 | public Optional getParent() { 70 | return Optional.ofNullable(parent); 71 | } 72 | 73 | /** 74 | * Get list of {@code Method} which are getters 75 | * @return list of getters 76 | */ 77 | public List getGetters() { 78 | return Lists.concat(getParentGetters(), getters); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/Enum.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast; 2 | 3 | import java.util.List; 4 | 5 | import com.github.havardh.javaflow.model.CanonicalName; 6 | 7 | /** 8 | * Internal representation of enum types 9 | */ 10 | public class Enum extends Type { 11 | 12 | private final List values; 13 | 14 | /** 15 | * Create a {@code Enum} with a name and a list of allowed values 16 | * 17 | * @param name the name 18 | * @param values the list of allowed values 19 | */ 20 | public Enum(CanonicalName name, List values) { 21 | super(name); 22 | this.values = values; 23 | } 24 | 25 | /** 26 | * Get the list of values 27 | * 28 | * @return the list of values 29 | */ 30 | public List getValues() { 31 | return values; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/Field.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast; 2 | 3 | import static java.lang.String.format; 4 | import static java.util.Objects.requireNonNull; 5 | 6 | import com.github.havardh.javaflow.model.CanonicalName; 7 | 8 | /** 9 | * Internal representation of a member field 10 | */ 11 | public class Field { 12 | 13 | private final boolean isNullable; 14 | private final String name; 15 | private final Type type; 16 | 17 | /** 18 | * Create a {@code Field} with the name, type and flag for nullable 19 | * 20 | * @param isNullable flag which is true when the type is nullable 21 | * @param name the name of the field 22 | * @param type the type of the field 23 | */ 24 | public Field(boolean isNullable, String name, Type type) { 25 | this.isNullable = isNullable; 26 | this.type = requireNonNull(type); 27 | this.name = requireNonNull(name); 28 | } 29 | 30 | /** 31 | * Get the type of the field 32 | * 33 | * @return the type of the field 34 | */ 35 | public Type getType() { 36 | return type; 37 | } 38 | 39 | /** 40 | * Get the name of the field 41 | * 42 | * @return the name of the field 43 | */ 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | /** 49 | * Check if the field is nullable 50 | * 51 | * @return true if the field is nullable 52 | */ 53 | public boolean isNullable() { 54 | return isNullable; 55 | } 56 | 57 | /** {@inheritDoc} */ 58 | @Override 59 | public String toString() { 60 | return format("%s: %s", name, type.getCanonicalName()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/List.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast; 2 | 3 | import com.github.havardh.javaflow.model.CanonicalName; 4 | 5 | /** 6 | * Internal representation of a list 7 | */ 8 | public class List extends Type { 9 | private final Type type; 10 | 11 | /** 12 | * Create a {@code List} with a name and a type for the values 13 | * 14 | * @param name the name of this type 15 | * @param type the type of the value 16 | */ 17 | public List(CanonicalName name, Type type) { 18 | super(name); 19 | this.type = type; 20 | } 21 | 22 | /** 23 | * Get the type of the values 24 | * 25 | * @return the type of the value 26 | */ 27 | public Type getType() { 28 | return type; 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/Map.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast; 2 | 3 | import com.github.havardh.javaflow.model.CanonicalName; 4 | 5 | /** 6 | * Internal representation of a key value map. 7 | */ 8 | public class Map extends Type { 9 | private final Type key; 10 | private final Type value; 11 | 12 | /** 13 | * Create a {@code Map} with a name and a type for the key 14 | * and for the value 15 | * 16 | * @param name the name of this type 17 | * @param key the type of the key 18 | * @param value the type of the value 19 | */ 20 | public Map(CanonicalName name, Type key, Type value) { 21 | super(name); 22 | this.key = key; 23 | this.value = value; 24 | } 25 | 26 | /** 27 | * Get the type of the keys 28 | * 29 | * @return the type of the key 30 | */ 31 | public Type getKeyType() { 32 | return key; 33 | } 34 | 35 | /** 36 | * Get the type of the values 37 | * 38 | * @return the type of the value 39 | */ 40 | public Type getValueType() { 41 | return value; 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/Method.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast; 2 | 3 | import static java.lang.String.format; 4 | 5 | public class Method { 6 | private final String name; 7 | private final Type type; 8 | 9 | public Method(String name, Type type) { 10 | this.name = name; 11 | this.type = type; 12 | } 13 | 14 | public String getName() { 15 | return name; 16 | } 17 | 18 | public Type getType() { 19 | return type; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return format("%s: %s", name, getType()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/Parent.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast; 2 | 3 | import static java.util.Collections.emptyList; 4 | import static java.util.Optional.ofNullable; 5 | 6 | import java.util.List; 7 | 8 | import com.github.havardh.javaflow.model.CanonicalName; 9 | 10 | /** 11 | * Internal representation of a parent link 12 | * 13 | * A {@code Parent} link is created during the parsing phase 14 | * with the canonical name of the referenced type. During the 15 | * {@code InheritanceTransformer} phase the {@code reference} 16 | * to the actual internal representation for the link is updated. 17 | */ 18 | public class Parent extends Type { 19 | 20 | private Class reference; 21 | 22 | /** 23 | * Create a parent link for a {@code CanonicalName} 24 | * 25 | * @param name the canonical name to refer to 26 | */ 27 | public Parent(CanonicalName name) { 28 | super(name); 29 | } 30 | 31 | public List getFields() { 32 | return ofNullable(reference).map(Class::getFields).orElse(emptyList()); 33 | } 34 | 35 | public List getGetters() { 36 | return ofNullable(reference).map(Class::getGetters).orElse(emptyList()); 37 | } 38 | 39 | /** 40 | * Set reference 41 | * 42 | * @param reference the class of the type to reference 43 | */ 44 | public void setReference(Class reference) { 45 | this.reference = reference; 46 | } 47 | 48 | /** 49 | * Get the {@code Class} referenced to 50 | * 51 | * @return the {@code Class} reference 52 | */ 53 | public Class getReference() { 54 | return reference; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/Type.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.github.havardh.javaflow.model.CanonicalName; 6 | 7 | /** 8 | * The internal representation of a model 9 | */ 10 | public class Type { 11 | 12 | protected final CanonicalName name; 13 | 14 | /** 15 | * Create a {@code Type} with a given {@code CanonicalName} 16 | * 17 | * @param name the canonical name for the type 18 | */ 19 | public Type(CanonicalName name) { 20 | this.name = name; 21 | } 22 | 23 | /** 24 | * Get the canonical name of type 25 | * 26 | * @return the canonical name of the type 27 | */ 28 | public CanonicalName getCanonicalName() { 29 | return name; 30 | } 31 | 32 | /** {@inheritDoc} */ 33 | @Override 34 | public boolean equals(Object o) { 35 | if (this == o) { 36 | return true; 37 | } 38 | if (o == null || getClass() != o.getClass()) { 39 | return false; 40 | } 41 | 42 | Type type = (Type) o; 43 | 44 | return name != null ? name.equals(type.name) : type.name == null; 45 | } 46 | 47 | /** {@inheritDoc} */ 48 | @Override 49 | public int hashCode() { 50 | return name != null ? name.hashCode() : 0; 51 | } 52 | 53 | /** {@inheritDoc} */ 54 | @Override 55 | public String toString() { 56 | return format("%s", name); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/builders/Builder.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast.builders; 2 | 3 | /** 4 | * Interface for builders of the type {@code T}. 5 | * 6 | * @param the type to build 7 | */ 8 | public interface Builder { 9 | 10 | /** 11 | * Build a {@code T} type 12 | * 13 | * @return the new {@code T} 14 | */ 15 | T build(); 16 | 17 | } -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/builders/ClassBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast.builders; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.github.havardh.javaflow.ast.Class; 7 | import com.github.havardh.javaflow.ast.Field; 8 | import com.github.havardh.javaflow.ast.Method; 9 | import com.github.havardh.javaflow.ast.Parent; 10 | import com.github.havardh.javaflow.ast.Type; 11 | import com.github.havardh.javaflow.model.CanonicalName; 12 | 13 | /** 14 | * Builder for {@code Class} objects. 15 | */ 16 | public class ClassBuilder implements Builder { 17 | 18 | private String packageName; 19 | private String name; 20 | private Parent parent; 21 | private List innerClasses = new ArrayList<>(); 22 | private List fields = new ArrayList<>(); 23 | private List getters = new ArrayList<>(); 24 | 25 | private ClassBuilder() { 26 | } 27 | 28 | /** 29 | * Create an empty {@code ClassBuilder}. 30 | * 31 | * @return an empty {@code ClassBuilder} 32 | */ 33 | public static ClassBuilder classBuilder() { 34 | return new ClassBuilder(); 35 | } 36 | 37 | /** 38 | * Set the package name 39 | * 40 | * @param packageName the package name 41 | * @return the builder for method chaining 42 | */ 43 | public ClassBuilder withPackageName(String packageName) { 44 | this.packageName = packageName; 45 | return this; 46 | } 47 | 48 | /** 49 | * Set the name 50 | * 51 | * @param name the of the {@code Class} 52 | * @return the builder for method chaining 53 | */ 54 | public ClassBuilder withName(String name) { 55 | this.name = name; 56 | return this; 57 | } 58 | 59 | /** 60 | * Set the parent link 61 | * 62 | * @param parent the parent link for the {@code Class} 63 | * @return the builder for method chaining 64 | */ 65 | public ClassBuilder withParent(Parent parent) { 66 | this.parent = parent; 67 | return this; 68 | } 69 | 70 | /** 71 | * Add an inner {@code Class} to class builder 72 | * 73 | * @param innerClass an inner {@code Class} 74 | * @return the builder for method chaining 75 | */ 76 | public ClassBuilder withInnerClass(Class innerClass) { 77 | this.innerClasses.add(innerClass); 78 | return this; 79 | } 80 | 81 | /** 82 | * Add a {@code Field} to class builder 83 | * 84 | * @param field a {@code Field} 85 | * @return the builder for method chaining 86 | */ 87 | public ClassBuilder withField(Field field) { 88 | this.fields.add(field); 89 | return this; 90 | } 91 | 92 | /** 93 | * Add a {@code Method} to class builder 94 | * @param getterMethod a {$code Method} 95 | * @return the builder for method chaining 96 | */ 97 | public ClassBuilder withGetter(Method getterMethod) { 98 | this.getters.add(getterMethod); 99 | return this; 100 | } 101 | 102 | /** 103 | * Build a {@code Class} with the builder configuration 104 | * 105 | * @return the {@code Class} 106 | */ 107 | public Class build() { 108 | return new Class(CanonicalName.object(packageName, name), parent, innerClasses, fields, getters); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/builders/EnumBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast.builders; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.github.havardh.javaflow.ast.Enum; 7 | import com.github.havardh.javaflow.model.CanonicalName; 8 | 9 | /** 10 | * Builder for {@code Enum} objects. 11 | */ 12 | public class EnumBuilder implements Builder { 13 | 14 | private String packageName; 15 | private String name; 16 | private List values = new ArrayList<>(); 17 | 18 | private EnumBuilder() { 19 | } 20 | 21 | /** 22 | * Create an empty {@code EnumBuilder}. 23 | * 24 | * @return an empty {@code EnumBuilder} 25 | */ 26 | public static EnumBuilder enumBuilder() { 27 | return new EnumBuilder(); 28 | } 29 | 30 | /** 31 | * Set the package name 32 | * 33 | * @param packageName the package name 34 | * @return the builder for method chaining 35 | */ 36 | public EnumBuilder withPackageName(String packageName) { 37 | this.packageName = packageName; 38 | return this; 39 | } 40 | 41 | /** 42 | * Set the name 43 | * 44 | * @param name the name of the enum 45 | * @return the builder for method chaining 46 | */ 47 | public EnumBuilder withName(String name) { 48 | this.name = name; 49 | return this; 50 | } 51 | 52 | /** 53 | * Add a valid enum value 54 | * 55 | * @param name the valid enum value to add 56 | * @return the builder for method chaining 57 | */ 58 | public EnumBuilder withEnumValue(String name) { 59 | this.values.add(name); 60 | return this; 61 | } 62 | 63 | /** 64 | * Build a {@code Enum} with the current configuration 65 | * 66 | * @return the {@code Enum} 67 | */ 68 | public Enum build() { 69 | return new Enum(CanonicalName.object(packageName, name), values); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/builders/FieldBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast.builders; 2 | 3 | import com.github.havardh.javaflow.ast.Field; 4 | import com.github.havardh.javaflow.ast.Type; 5 | 6 | /** 7 | * Builder for {@code Field} objects 8 | */ 9 | public final class FieldBuilder { 10 | private boolean isNullable; 11 | private String name; 12 | private Type type; 13 | 14 | private FieldBuilder() { 15 | } 16 | 17 | /** 18 | * Create an empty field builder 19 | * 20 | * @return the empty field builder 21 | */ 22 | public static FieldBuilder fieldBuilder() { 23 | return new FieldBuilder(); 24 | } 25 | 26 | /** 27 | * Set the isNullable value 28 | * 29 | * @param isNullable set to true if the type is nullable 30 | * @return the builder for method chaining 31 | */ 32 | public FieldBuilder withIsNullable(boolean isNullable) { 33 | this.isNullable = isNullable; 34 | return this; 35 | } 36 | 37 | /** 38 | * Set the name 39 | * 40 | * @param name the name of the field 41 | * @return the builder for method chaining 42 | */ 43 | public FieldBuilder withName(String name) { 44 | this.name = name; 45 | return this; 46 | } 47 | 48 | /** 49 | * Set the type 50 | * 51 | * @param type the type of the field 52 | * @return the builder for method chaining 53 | */ 54 | public FieldBuilder withType(Type type) { 55 | this.type = type; 56 | return this; 57 | } 58 | 59 | /** 60 | * Build a {@code Field} with the current configuration 61 | * 62 | * @return the {@code Field} 63 | */ 64 | public Field build() { 65 | return new Field(isNullable, name, type); 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/builders/MethodBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast.builders; 2 | 3 | import com.github.havardh.javaflow.ast.Method; 4 | import com.github.havardh.javaflow.ast.Type; 5 | 6 | public class MethodBuilder implements Builder { 7 | 8 | private String name; 9 | private Type type; 10 | 11 | public static MethodBuilder methodBuilder() { 12 | return new MethodBuilder(); 13 | } 14 | 15 | public MethodBuilder withName(String name) { 16 | this.name = name; 17 | return this; 18 | } 19 | 20 | public MethodBuilder withType(Type type) { 21 | this.type = type; 22 | return this; 23 | } 24 | 25 | /** 26 | * Build a {@code Method} with the builder configuration 27 | * 28 | * @return the {@code Method} 29 | */ 30 | @Override 31 | public Method build() { 32 | return new Method(name, type); 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/ast/builders/TypeBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.ast.builders; 2 | 3 | import com.github.havardh.javaflow.ast.List; 4 | import com.github.havardh.javaflow.ast.Map; 5 | import com.github.havardh.javaflow.model.CanonicalName; 6 | import com.github.havardh.javaflow.ast.Type; 7 | 8 | /** 9 | * Builder for {@code Type} objects 10 | */ 11 | public final class TypeBuilder { 12 | protected CanonicalName name; 13 | 14 | private Type firstType; 15 | private Type secondType; 16 | 17 | 18 | private TypeBuilder() { 19 | } 20 | 21 | /** 22 | * Create an empty {@code TypeBuilder} 23 | * 24 | * @return empty {@code TypeBuilder} 25 | */ 26 | public static TypeBuilder type() { 27 | return new TypeBuilder(); 28 | } 29 | 30 | /** 31 | * Factory method for creating a primitive type for the given name 32 | * 33 | * @param name the name of the type 34 | * @return a {@code Type} with the name 35 | */ 36 | public static Type primitive(CanonicalName name) { 37 | return new Type(name); 38 | } 39 | 40 | /** 41 | * Factory method for creating an object type for the given name 42 | * 43 | * @param name the name of the type 44 | * @return a {@code Type} with the name 45 | */ 46 | public static Type object(CanonicalName name) { 47 | return new Type(name); 48 | } 49 | 50 | /** 51 | * Factory method for creating a list type for the given name and value type name 52 | * 53 | * @param name the name of the type 54 | * @param type the type of the value 55 | * @return a {@code List} with the name and value type 56 | */ 57 | public static Type list(CanonicalName name, Type type) { 58 | return new List(name, type); 59 | } 60 | 61 | /** 62 | * Factory method for creating a map type with a given name, key and value. 63 | * 64 | * @param name the name of the type 65 | * @param key the type of the keys in the map 66 | * @param value the type of the values in the map 67 | * @return the type for the map 68 | */ 69 | public static Type map(CanonicalName name, Type key, Type value) { 70 | return new Map(name, key, value); 71 | } 72 | 73 | /** 74 | * Set the name of the builder 75 | * 76 | * @param name the name of the type 77 | * @return the builder for method chaining 78 | */ 79 | public TypeBuilder withName(CanonicalName name) { 80 | this.name = name; 81 | return this; 82 | } 83 | 84 | /** 85 | * Set the type of a list type 86 | * 87 | * @param type the type of the list values 88 | * @return the builder for method chaining 89 | */ 90 | public TypeBuilder withListType(Type type) { 91 | this.firstType = type; 92 | this.secondType = null; 93 | return this; 94 | } 95 | 96 | /** 97 | * Set the types of a map type 98 | * 99 | * @param key the type of the keys in the map 100 | * @param value the type of the values of the map 101 | * @return the builder for method chaining 102 | */ 103 | public TypeBuilder withMapType( 104 | Type key, 105 | Type value 106 | ) { 107 | this.firstType = key; 108 | this.secondType = value; 109 | return this; 110 | } 111 | 112 | /** 113 | * Build a type with the current configuration 114 | * 115 | * @return the type 116 | */ 117 | public Type build() { 118 | if (secondType != null) { 119 | return map(name, firstType, secondType); 120 | } else if (firstType != null) { 121 | return list(name, firstType); 122 | } else { 123 | return object(name); 124 | } 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/exceptions/AggregatedException.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.exceptions; 2 | 3 | import static java.lang.String.format; 4 | import static java.util.stream.Collectors.joining; 5 | 6 | import java.util.List; 7 | import java.util.stream.IntStream; 8 | 9 | public class AggregatedException extends RuntimeException { 10 | 11 | private final boolean showEnumerationInErrors; 12 | private final List exceptions; 13 | 14 | public AggregatedException(String message, List exceptions, boolean showEnumerationInErrors) { 15 | super(message); 16 | this.exceptions = exceptions; 17 | this.showEnumerationInErrors = showEnumerationInErrors; 18 | } 19 | 20 | public List getExceptions() { 21 | return exceptions; 22 | } 23 | 24 | public String getMessage() { 25 | return super.getMessage() + "\n" + this.getSubMessages(); 26 | } 27 | 28 | private String getSubMessages() { 29 | return IntStream 30 | .range(0, exceptions.size()) 31 | .mapToObj(i -> getSubmessagePrefix(i) + exceptions.get(i).getMessage()) 32 | .collect(joining("\n\n")); 33 | } 34 | 35 | private String getSubmessagePrefix(int errorNumber) { 36 | return showEnumerationInErrors ? format("%d) ", errorNumber + 1) : ""; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/exceptions/ExitException.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.exceptions; 2 | 3 | public class ExitException extends RuntimeException { 4 | public enum ErrorCode { 5 | COULD_NOT_PARSE_TYPE_MAP(1), 6 | COULD_NOT_PARSE_SOURCE_CODE(2), 7 | COULD_NOT_READ_FILE(3), 8 | COULD_NOT_RESOLVE_PATTERN(4), 9 | COULD_NOT_WRITE_OUTPUT(5); 10 | 11 | private final int code; 12 | 13 | ErrorCode(int code) { 14 | this.code = code; 15 | } 16 | 17 | public int getCode() { 18 | return code; 19 | } 20 | } 21 | 22 | private final ErrorCode errorCode; 23 | 24 | public ExitException(ErrorCode errorCode, Throwable cause) { 25 | super(errorCode.name(), cause); 26 | this.errorCode = errorCode; 27 | } 28 | 29 | public ErrorCode getErrorCode() { 30 | return errorCode; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/exceptions/FieldGettersMismatchException.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.exceptions; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.github.havardh.javaflow.model.CanonicalName; 6 | 7 | public class FieldGettersMismatchException extends RuntimeException { 8 | public FieldGettersMismatchException(CanonicalName className, String message) { 9 | super(format("Model {%s} is not a pure DTO.\n", className) + message); 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/exceptions/MissingTypeException.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.exceptions; 2 | 3 | import static java.lang.String.format; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import com.github.havardh.javaflow.ast.Field; 9 | import com.github.havardh.javaflow.ast.Type; 10 | 11 | /** 12 | * An exception type to be thrown if a type is used is not found in the 13 | * input model or in the context of JavaFlow. 14 | */ 15 | public class MissingTypeException extends RuntimeException { 16 | 17 | private final Map> types; 18 | 19 | /** 20 | * Creates a missing type exception 21 | * 22 | * @param types a map from type to fields with missing types 23 | */ 24 | public MissingTypeException(Map> types) { 25 | super(format("Could not find types: %s", types)); 26 | this.types = types; 27 | } 28 | 29 | /** 30 | * Get details of exception 31 | * 32 | * @return the map from type to fields with missing types 33 | */ 34 | public Map> getTypes() { 35 | return types; 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/model/CanonicalName.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import static java.lang.String.format; 4 | 5 | import static com.github.havardh.javaflow.model.builders.CanonicalNameBuilder.canonicalName; 6 | 7 | /** 8 | * A representation of a canonical name. This name should uniquely 9 | * identify a type. This class is used as the identifier for all 10 | * of the {@code Type} classes in JavaFlow. 11 | * 12 | * The {@code CanonicalName} consists of an optional package name and 13 | * a require name. 14 | */ 15 | public class CanonicalName { 16 | 17 | private final String packageName; 18 | private final String name; 19 | 20 | /** 21 | * Constructs a CanonicalName 22 | * 23 | * @param packageName - the package name 24 | * @param name - the name 25 | */ 26 | private CanonicalName(String packageName, String name) { 27 | this.packageName = packageName; 28 | this.name = name; 29 | } 30 | 31 | /** 32 | * Parses a CanonicalName from a string representation of 33 | * a canonical name. 34 | * 35 | * @param canonicalName canonical name as a string 36 | * @return a instance of {@code CanonicalName} 37 | */ 38 | public static CanonicalName fromString(String canonicalName) { 39 | int lastDot = canonicalName.lastIndexOf('.'); 40 | 41 | return canonicalName() 42 | .withPackageName(canonicalName.substring(0, lastDot)) 43 | .withName(canonicalName.substring(lastDot+1, canonicalName.length())) 44 | .build(); 45 | } 46 | 47 | /** 48 | * Factory for create a {@code CanonicalName} for a primitive type 49 | * 50 | * @param name the name of the primitive type 51 | * @return the {@code CanonicalName} for the primitive type 52 | */ 53 | public static CanonicalName primitive(String name) { 54 | return new CanonicalName(null, name); 55 | } 56 | 57 | /** 58 | * Factory for create a {@code CanonicalName} for an object 59 | * 60 | * @param packageName the package name for the object 61 | * @param name the class name of the object 62 | * @return the {@code CanonicalName} for the object 63 | */ 64 | public static CanonicalName object(String packageName, String name) { 65 | return new CanonicalName(packageName, name); 66 | } 67 | 68 | /** 69 | * Get the name of the CanonicalName 70 | * 71 | * @return the name 72 | */ 73 | public String getName() { 74 | return name; 75 | } 76 | 77 | /** 78 | * Get the package name of the CanonicalName 79 | * 80 | * @return the package name 81 | */ 82 | public String getPackageName() { 83 | return packageName; 84 | } 85 | 86 | /** {@inheritDoc} */ 87 | @Override 88 | public boolean equals(Object o) { 89 | if (this == o) { 90 | return true; 91 | } 92 | if (o == null || getClass() != o.getClass()) { 93 | return false; 94 | } 95 | 96 | CanonicalName that = (CanonicalName) o; 97 | 98 | if (packageName != null ? !packageName.equals(that.packageName) : that.packageName != null) { 99 | return false; 100 | } 101 | return name != null ? name.equals(that.name) : that.name == null; 102 | 103 | } 104 | 105 | /** {@inheritDoc} */ 106 | @Override 107 | public int hashCode() { 108 | int result = packageName != null ? packageName.hashCode() : 0; 109 | result = 31 * result + (name != null ? name.hashCode() : 0); 110 | return result; 111 | } 112 | 113 | /** {@inheritDoc} */ 114 | @Override 115 | public String toString() { 116 | return packageName != null 117 | ? format("%s.%s", packageName, name) 118 | : name; 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/model/TypeMap.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import static java.util.Collections.emptyMap; 4 | 5 | import java.io.FileNotFoundException; 6 | import java.io.FileReader; 7 | import java.io.IOException; 8 | import java.util.Collection; 9 | import java.util.Collections; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | import com.esotericsoftware.yamlbeans.YamlReader; 14 | import com.github.havardh.javaflow.exceptions.ExitException; 15 | import com.github.havardh.javaflow.exceptions.ExitException.ErrorCode; 16 | 17 | /** 18 | * Type map class to contain the map from source language types to 19 | * flow types. 20 | */ 21 | public class TypeMap implements Map { 22 | 23 | /** 24 | * Factory method to create an empty {@code TypeMap} 25 | * 26 | * @return empty {@code TypeMap} 27 | */ 28 | public static TypeMap emptyTypeMap() { 29 | return new TypeMap(emptyMap()); 30 | } 31 | 32 | private Map map; 33 | 34 | /** 35 | * Create a {@code TypeMap} based on the given {@code Map} 36 | * 37 | * @param map the map to create the {@code TypeMap} from. 38 | */ 39 | public TypeMap(Map map) { 40 | this.map = map; 41 | } 42 | 43 | public TypeMap(String filename) { 44 | try (FileReader fileReader = new FileReader(filename)) { 45 | YamlReader yamlReader = new YamlReader(fileReader); 46 | 47 | map = (Map)yamlReader.read(); 48 | } catch (FileNotFoundException e) { 49 | map = emptyMap(); 50 | } catch (IOException e) { 51 | throw new ExitException(ErrorCode.COULD_NOT_PARSE_TYPE_MAP, e); 52 | } 53 | } 54 | 55 | public static TypeMap of(String k1, String v1) { 56 | return new TypeMap(Collections.singletonMap(k1, v1)); 57 | } 58 | 59 | /** {@inheritDoc} */ 60 | @Override 61 | public int size() { 62 | return map.size(); 63 | } 64 | 65 | /** {@inheritDoc} */ 66 | @Override 67 | public boolean isEmpty() { 68 | return map.isEmpty(); 69 | } 70 | 71 | /** {@inheritDoc} */ 72 | @Override 73 | public boolean containsKey(Object key) { 74 | return map.containsKey(key); 75 | } 76 | 77 | /** {@inheritDoc} */ 78 | @Override 79 | public boolean containsValue(Object value) { 80 | return map.containsValue(value); 81 | } 82 | 83 | /** {@inheritDoc} */ 84 | @Override 85 | public String get(Object key) { 86 | return map.get(key); 87 | } 88 | 89 | /** 90 | * Unsupported operation 91 | * 92 | * @throws UnsupportedOperationException always thrown 93 | */ 94 | @Override 95 | public String put(String key, String value) { 96 | throw new UnsupportedOperationException(); 97 | } 98 | 99 | /** 100 | * Unsupported operation 101 | * 102 | * @throws UnsupportedOperationException always thrown 103 | */ 104 | @Override 105 | public String remove(Object key) { 106 | throw new UnsupportedOperationException(); 107 | } 108 | 109 | /** 110 | * Unsupported operation 111 | * 112 | * @throws UnsupportedOperationException always thrown 113 | */ 114 | @Override 115 | public void putAll(Map m) { 116 | throw new UnsupportedOperationException(); 117 | } 118 | 119 | /** 120 | * Unsupported operation 121 | * 122 | * @throws UnsupportedOperationException always thrown 123 | */ 124 | @Override 125 | public void clear() { 126 | throw new UnsupportedOperationException(); 127 | } 128 | 129 | /** {@inheritDoc} */ 130 | @Override 131 | public Set keySet() { 132 | return map.keySet(); 133 | } 134 | 135 | /** {@inheritDoc} */ 136 | @Override 137 | public Collection values() { 138 | return map.values(); 139 | } 140 | 141 | /** {@inheritDoc} */ 142 | @Override 143 | public Set> entrySet() { 144 | return map.entrySet(); 145 | } 146 | } 147 | 148 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/model/builders/CanonicalNameBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model.builders; 2 | 3 | import com.github.havardh.javaflow.model.CanonicalName; 4 | 5 | /** 6 | * A builder for the type {@code CanonicalName}. 7 | */ 8 | public final class CanonicalNameBuilder { 9 | private String packageName; 10 | private String name; 11 | 12 | private CanonicalNameBuilder() { 13 | } 14 | 15 | /** 16 | * Create a empty builder instance 17 | * @return - a canonical name builder 18 | */ 19 | public static CanonicalNameBuilder canonicalName() { 20 | return new CanonicalNameBuilder(); 21 | } 22 | 23 | /** 24 | * Sets the package name on the builder 25 | * 26 | * @param packageName - the package name 27 | * @return - the builder itself 28 | */ 29 | public CanonicalNameBuilder withPackageName(String packageName) { 30 | this.packageName = packageName; 31 | return this; 32 | } 33 | 34 | /** 35 | * Sets the name on the builder 36 | * 37 | * @param name - the class name 38 | * @return - the builder itself 39 | */ 40 | public CanonicalNameBuilder withName(String name) { 41 | this.name = name; 42 | return this; 43 | } 44 | 45 | /** 46 | * Builds an instance of a {@code CanonicalName}. 47 | * 48 | * @return - a instance of {@code CanonicalName} 49 | */ 50 | public CanonicalName build() { 51 | return CanonicalName.object(packageName, name); 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/filetransform/CommentPrependTransformer.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.filetransform; 2 | 3 | /** 4 | * A {@code FileTranformer} to prepend a file with a comment 5 | */ 6 | public class CommentPrependTransformer implements FileTransformer { 7 | 8 | private final String comment; 9 | 10 | /** 11 | * Creates a {@code CommentPrependTransformer} with a 12 | * comment to prepend files with. 13 | * 14 | * @param comment the comment to prepend the file. 15 | */ 16 | public CommentPrependTransformer(String comment) { 17 | this.comment = comment; 18 | } 19 | 20 | /** 21 | * Prepend the file with the given comment 22 | * 23 | * @param file the file contents to transform 24 | * @return the file with the comment prepended to it 25 | */ 26 | @Override 27 | public String transform(String file) { 28 | return "/* " + comment + " */\n" + file; 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/filetransform/EslintDisableTransformer.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.filetransform; 2 | 3 | import static java.util.stream.Collectors.joining; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * A {@code FileTransformer} to append eslint-disable rules to 10 | * the beginning of an output file. 11 | */ 12 | public class EslintDisableTransformer implements FileTransformer { 13 | 14 | private List rules = new ArrayList<>(); 15 | 16 | /** 17 | * Create a {@code EslintDisableTranformer} with a list of rules 18 | * to disable. 19 | * 20 | * @param rules the list of rules to disable 21 | */ 22 | public EslintDisableTransformer(List rules) { 23 | this.rules = rules; 24 | } 25 | 26 | /** 27 | * Prepend eslint-disable to the given file 28 | * 29 | * @param file the file contents to transform 30 | * @return the file prepended with the disable rules 31 | */ 32 | @Override 33 | public String transform(String file) { 34 | String allRules = rules.stream() 35 | .map(rule -> "/* eslint-disable " + rule + " */") 36 | .collect(joining("\n")); 37 | 38 | return allRules + "\n" + file; 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/filetransform/FileTransformer.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.filetransform; 2 | 3 | /** 4 | * An interface for file transformers. 5 | * 6 | * {@code FileTransformer}s will look at a complete output 7 | * file and transform it to a new file 8 | */ 9 | public interface FileTransformer { 10 | 11 | /** 12 | * Transform the file contents to a new file. 13 | * 14 | * @param file the file contents to transform 15 | * @return the transformed file contents. 16 | */ 17 | String transform(String file); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/parser/Parser.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser; 2 | 3 | import java.util.List; 4 | 5 | import com.github.havardh.javaflow.ast.Type; 6 | 7 | /** 8 | * Interface for a code @{code Parser}. 9 | * 10 | * The parser should parse source code into the internal JavaFlow 11 | * representation of a model 12 | */ 13 | public interface Parser { 14 | 15 | /** 16 | * Parse the given source code into a list of {@code Type} objects. 17 | * 18 | * @param sourceCode the source to parse 19 | * @return the parsed source as a {@code List} 20 | */ 21 | List parse(String sourceCode); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/parser/java/CanonicalNameFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser.java; 2 | 3 | import java.util.Map; 4 | 5 | import com.github.havardh.javaflow.model.CanonicalName; 6 | import com.github.havardh.javaflow.util.Maps; 7 | 8 | /** 9 | * A Java specific factory for building a {@code CanonicalName}. 10 | * 11 | * The factory builds CanonicalNames for the the context or a java built-in type. 12 | * It will first resolve the name by checking if it is imported, then looking up 13 | * the typename in the list of built-in types for {@code java.long}. If it does not 14 | * find the name in the imports or built-in types it will give the type the default 15 | * package name which should be equal to the package name of the model the type was 16 | * used in. 17 | * 18 | * The list of built-in types are needed as Java does not require explicit imports of 19 | * these types in a Java program. 20 | */ 21 | public class CanonicalNameFactory { 22 | 23 | private static Map BUILTIN = Maps.collect( 24 | Maps.entry("Boolean", "java.lang"), 25 | Maps.entry("Byte", "java.lang"), 26 | Maps.entry("Character", "java.lang"), 27 | Maps.entry("Double", "java.lang"), 28 | Maps.entry("Float", "java.lang"), 29 | Maps.entry("Integer", "java.lang"), 30 | Maps.entry("Long", "java.lang"), 31 | Maps.entry("Short", "java.lang"), 32 | Maps.entry("String", "java.lang") 33 | ); 34 | 35 | private final String packageName; 36 | private final Map imports; 37 | 38 | /** 39 | * Sets up a factory for the given package name and list of imports. The factory 40 | * holds an internal list of Java built-in types from the {@code java.lang} package. 41 | * 42 | * @param packageName the package name 43 | * @param imports the list of imports 44 | */ 45 | public CanonicalNameFactory(String packageName, Map imports) { 46 | this.packageName = packageName; 47 | this.imports = imports; 48 | } 49 | 50 | /** 51 | * Build a canonical name for the given name. 52 | * 53 | * @param name the name to build a {@code CanonicalName} for 54 | * @return the {©code CanonicalName} for the given {@code name} 55 | */ 56 | CanonicalName build(String name) { 57 | String packageName = imports.getOrDefault(name, BUILTIN.getOrDefault(name, this.packageName)); 58 | return CanonicalName.object(packageName, name); 59 | } 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/parser/java/ClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser.java; 2 | 3 | import static java.util.Arrays.asList; 4 | import static java.util.stream.Collectors.joining; 5 | 6 | import java.util.ArrayDeque; 7 | import java.util.Deque; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import java.util.stream.Stream; 12 | 13 | import com.github.havardh.javaflow.ast.Field; 14 | import com.github.havardh.javaflow.ast.Method; 15 | import com.github.havardh.javaflow.ast.Parent; 16 | import com.github.havardh.javaflow.ast.builders.ClassBuilder; 17 | 18 | import com.github.javaparser.ast.CompilationUnit; 19 | import com.github.javaparser.ast.ImportDeclaration; 20 | import com.github.javaparser.ast.Modifier; 21 | import com.github.javaparser.ast.PackageDeclaration; 22 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; 23 | import com.github.javaparser.ast.body.FieldDeclaration; 24 | import com.github.javaparser.ast.body.MethodDeclaration; 25 | import com.github.javaparser.ast.expr.AnnotationExpr; 26 | import com.github.javaparser.ast.expr.Name; 27 | import com.github.javaparser.ast.type.PrimitiveType; 28 | import com.github.javaparser.ast.visitor.VoidVisitorAdapter; 29 | 30 | /** 31 | * A JavaFlow visitor for the {@code enum} java declaration. 32 | */ 33 | public class ClassVisitor extends VoidVisitorAdapter { 34 | 35 | private final boolean includeStaticFields; 36 | 37 | private String packageName; 38 | private Map imports = new HashMap<>(); 39 | private Deque classNames = new ArrayDeque<>(); 40 | 41 | public ClassVisitor(boolean includeStaticFields) { 42 | this.includeStaticFields = includeStaticFields; 43 | } 44 | 45 | /** {@inheritDoc} */ 46 | @Override 47 | public void visit(PackageDeclaration n, ClassBuilder builder) { 48 | super.visit(n, builder); 49 | 50 | packageName = n.getNameAsString(); 51 | builder.withPackageName(n.getNameAsString()); 52 | } 53 | 54 | /** {@inheritDoc} */ 55 | @Override 56 | public void visit(ImportDeclaration n, ClassBuilder builder) { 57 | super.visit(n, builder); 58 | 59 | String typeName = n.getName().getIdentifier(); 60 | String packageName = n.getName().getQualifier().map(Name::asString).orElse(""); 61 | 62 | imports.put(typeName, packageName); 63 | } 64 | 65 | /** {@inheritDoc} */ 66 | @Override 67 | public void visit(ClassOrInterfaceDeclaration n, ClassBuilder builder) { 68 | classNames.addLast(n.getNameAsString()); 69 | 70 | if (n.getParentNode().isPresent() && n.getParentNode().get() instanceof CompilationUnit) { 71 | super.visit(n, builder); 72 | setAttributes(builder, n); 73 | } else { 74 | String fullPackageName = packageName + "." 75 | + classNames.stream().limit(classNames.size() - 1).collect(joining(".")); 76 | 77 | imports.put(n.getNameAsString(), fullPackageName); 78 | 79 | ClassBuilder child = ClassBuilder.classBuilder(); 80 | child.withPackageName(fullPackageName); 81 | setAttributes(child, n); 82 | super.visit(n, child); 83 | builder.withInnerClass(child.build()); 84 | } 85 | 86 | classNames.removeLast(); 87 | } 88 | 89 | private void setAttributes(ClassBuilder builder, ClassOrInterfaceDeclaration n) { 90 | builder.withName(n.getNameAsString()); 91 | if (isClass(n)) { 92 | CanonicalNameFactory factory = new CanonicalNameFactory(packageName, imports); 93 | n.getExtendedTypes().stream().findFirst() 94 | .ifPresent(parent -> builder.withParent(new Parent(factory.build(parent.getNameAsString())))); 95 | } 96 | } 97 | 98 | /** {@inheritDoc} */ 99 | @Override 100 | public void visit(FieldDeclaration field, ClassBuilder builder) { 101 | super.visit(field, builder); 102 | TypeFactory factory = new TypeFactory(packageName, imports); 103 | 104 | if (shouldIncludeStaticFields(field.getModifiers())) { 105 | field.getVariables().forEach(variable -> builder.withField(new Field( 106 | isNullable(field), 107 | variable.getNameAsString(), 108 | factory.build(variable.getType().asString(), variable.getType() instanceof PrimitiveType) 109 | ))); 110 | } 111 | } 112 | 113 | @Override 114 | public void visit(MethodDeclaration method, ClassBuilder builder) { 115 | super.visit(method, builder); 116 | TypeFactory factory = new TypeFactory(packageName, imports); 117 | 118 | if (method.getParentNode().isPresent() 119 | && method.getParentNode().get() instanceof ClassOrInterfaceDeclaration 120 | && isClass((ClassOrInterfaceDeclaration) method.getParentNode().get()) 121 | && isGetter(method.getNameAsString(), method.getType().asString()) 122 | && shouldIncludeStaticFields(method.getModifiers())) { 123 | builder.withGetter(new Method( 124 | method.getNameAsString(), 125 | factory.build(method.getType().asString(), method.getType() instanceof PrimitiveType) 126 | )); 127 | } 128 | } 129 | 130 | private boolean shouldIncludeStaticFields(Set modifiers) { 131 | return includeStaticFields || !modifiers.contains(Modifier.STATIC); 132 | } 133 | 134 | private boolean isNullable(FieldDeclaration field) { 135 | return field.getAnnotations().stream() 136 | .map(AnnotationExpr::getNameAsString) 137 | .anyMatch(name -> name.equals("Nullable")); 138 | } 139 | 140 | private boolean isClass(ClassOrInterfaceDeclaration classOrInterfaceDeclaration) { 141 | return !classOrInterfaceDeclaration.isInterface(); 142 | } 143 | 144 | private boolean isGetter(String name, String type) { 145 | if (asList("boolean", "Boolean").contains(type) && name.startsWith("is")) { 146 | return true; 147 | } 148 | 149 | return name.startsWith("get"); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/parser/java/EnumVisitor.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser.java; 2 | 3 | import com.github.havardh.javaflow.ast.builders.EnumBuilder; 4 | 5 | import com.github.javaparser.ast.PackageDeclaration; 6 | import com.github.javaparser.ast.body.EnumConstantDeclaration; 7 | import com.github.javaparser.ast.body.EnumDeclaration; 8 | import com.github.javaparser.ast.visitor.VoidVisitorAdapter; 9 | 10 | /** 11 | * A JavaFlow visitor for the {@code enum} java declaration. 12 | */ 13 | public class EnumVisitor extends VoidVisitorAdapter { 14 | 15 | /** {@inheritDoc} */ 16 | @Override 17 | public void visit(PackageDeclaration n, EnumBuilder builder) { 18 | super.visit(n, builder); 19 | builder.withPackageName(n.getNameAsString()); 20 | } 21 | 22 | /** {@inheritDoc} */ 23 | @Override 24 | public void visit(EnumDeclaration n, EnumBuilder builder) { 25 | super.visit(n, builder); 26 | builder.withName(n.getNameAsString()); 27 | } 28 | 29 | /** {@inheritDoc} */ 30 | @Override 31 | public void visit(EnumConstantDeclaration n, EnumBuilder builder) { 32 | super.visit(n, builder); 33 | builder.withEnumValue(n.getNameAsString()); 34 | } 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/parser/java/JavaParser.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser.java; 2 | 3 | import static com.github.havardh.javaflow.ast.builders.ClassBuilder.classBuilder; 4 | import static com.github.havardh.javaflow.ast.builders.EnumBuilder.enumBuilder; 5 | import static com.github.havardh.javaflow.phases.parser.java.JavaParserFlags.defaultFlags; 6 | 7 | import java.io.StringReader; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | 13 | import com.github.havardh.javaflow.ast.Class; 14 | import com.github.havardh.javaflow.ast.Type; 15 | import com.github.havardh.javaflow.ast.builders.Builder; 16 | import com.github.havardh.javaflow.exceptions.ExitException; 17 | import com.github.havardh.javaflow.exceptions.ExitException.ErrorCode; 18 | import com.github.havardh.javaflow.phases.parser.Parser; 19 | 20 | import com.github.javaparser.ParseProblemException; 21 | import com.github.javaparser.ast.CompilationUnit; 22 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; 23 | import com.github.javaparser.ast.body.EnumDeclaration; 24 | import com.github.javaparser.ast.visitor.VoidVisitor; 25 | 26 | /** 27 | * {@code Parser} for parsing Java source code into {@code Type}. 28 | */ 29 | public class JavaParser implements Parser { 30 | 31 | private final JavaParserFlags flags; 32 | 33 | public JavaParser() { 34 | flags = defaultFlags(); 35 | } 36 | 37 | public JavaParser(JavaParserFlags flags) { 38 | this.flags = flags; 39 | } 40 | 41 | /** 42 | * Parse a Java model into the internal representation of a model. 43 | * 44 | * The Java model parser parses {@code class} or {@code enum} definitions 45 | * into a list of {@code Type} objects. 46 | * 47 | * @param source the Java source code for a model 48 | * @return the parsed source code as a {@code List}. 49 | */ 50 | public List parse(String source) { 51 | 52 | try { 53 | CompilationUnit cu = com.github.javaparser.JavaParser.parse(new StringReader(source)); 54 | return convert(cu); 55 | } catch (ParseProblemException e) { 56 | throw new ExitException(ErrorCode.COULD_NOT_PARSE_SOURCE_CODE, e); 57 | } 58 | } 59 | 60 | private List convert(CompilationUnit cu) { 61 | 62 | if (containsClass(cu)) { 63 | return convert(cu, classBuilder(), new ClassVisitor(flags.shouldIncludeStaticFields())); 64 | } else if (containsEnum(cu)) { 65 | return convert(cu, enumBuilder(), new EnumVisitor()); 66 | } else { 67 | return Collections.emptyList(); 68 | } 69 | } 70 | 71 | private static List convert( 72 | CompilationUnit cu, 73 | Builder builder, 74 | VoidVisitor visitor 75 | ) { 76 | visitor.visit(cu, builder); 77 | return toTypes(builder.build()).collect(Collectors.toList()); 78 | } 79 | 80 | private static Stream toTypes(Type type) { 81 | if (type instanceof Class) { 82 | return Stream.concat(Stream.of(type), ((Class) type).getInnerClasses().stream().flatMap(JavaParser::toTypes)); 83 | } else { 84 | return Stream.of(type); 85 | } 86 | } 87 | 88 | private static boolean containsEnum(CompilationUnit cu) { 89 | return cu.getTypes().stream() 90 | .anyMatch(t -> t instanceof EnumDeclaration); 91 | } 92 | 93 | private static boolean containsClass(CompilationUnit cu) { 94 | return cu.getTypes().stream() 95 | .anyMatch(t -> t instanceof ClassOrInterfaceDeclaration); 96 | } 97 | 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/parser/java/JavaParserFlags.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser.java; 2 | 3 | public class JavaParserFlags { 4 | private boolean includeStaticFields; 5 | 6 | private JavaParserFlags(boolean includeStaticFields) { 7 | this.includeStaticFields = includeStaticFields; 8 | } 9 | 10 | public static JavaParserFlags defaultFlags() { 11 | return Builder.withDefaults().build(); 12 | } 13 | 14 | boolean shouldIncludeStaticFields() { 15 | return includeStaticFields; 16 | } 17 | 18 | public static Builder flagsBuilder() { 19 | return new Builder(); 20 | } 21 | 22 | public static class Builder { 23 | 24 | private boolean includeStaticFields; 25 | 26 | static Builder withDefaults() { 27 | return new Builder().includeStaticFields(false); 28 | } 29 | 30 | public Builder includeStaticFields(boolean includeStaticFields) { 31 | this.includeStaticFields = includeStaticFields; 32 | return this; 33 | } 34 | 35 | public JavaParserFlags build() { 36 | return new JavaParserFlags(includeStaticFields); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/parser/java/TypeFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser.java; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import com.github.havardh.javaflow.ast.Type; 8 | import com.github.havardh.javaflow.ast.builders.TypeBuilder; 9 | import com.github.havardh.javaflow.model.CanonicalName; 10 | 11 | /** 12 | * A factory for {@code Type} used to create these objects from 13 | * Java models. 14 | */ 15 | public class TypeFactory { 16 | 17 | private static final List PRIMITIVES = Arrays.asList("boolean", "byte", 18 | "char", "double", "float", "int", "long", "short"); 19 | 20 | private CanonicalNameFactory canonicalNameFactory; 21 | 22 | /** 23 | * Constructs a {@code TypeFactory} based on the a package name and a list 24 | * of imports. 25 | * 26 | * The types build by this factory will have their names assigned based on 27 | * the package name and or the imported name. The package name should be 28 | * the package name for the model the factory is used for. The list of 29 | * imported names should be equal to the list of imports in the model the 30 | * factory is used for. 31 | * 32 | * @param packageName the package name for the model to use the factory for 33 | * @param imports the list of imports for the model to use the factory for 34 | */ 35 | public TypeFactory(String packageName, Map imports) { 36 | this.canonicalNameFactory = new CanonicalNameFactory(packageName, imports); 37 | } 38 | 39 | /** 40 | * Builds a {@code Type} from the given type literal and is primitive flag. 41 | * 42 | * The canonical name for the {@code Type} is resolved based on the context of 43 | * the factory. If the name used in the model is found in the import the canonical 44 | * name is build using the imported name. If the name is not imported the {@code Type} 45 | * is given the same package name as the factory is initialized for. 46 | * 47 | * @param typeLiteral the literal name for the type used in the model 48 | * @param isPrimitive a flag saying if the type is a primitive or an object 49 | * @return the {@code Type} representation for type literal 50 | */ 51 | public Type build(String typeLiteral, boolean isPrimitive) { 52 | if (isList(typeLiteral) || isSet(typeLiteral)) { 53 | String tagType = extractTagType(typeLiteral); 54 | String valType = extractType(typeLiteral); 55 | return TypeBuilder.list( 56 | canonicalNameFactory.build(tagType), 57 | build(valType, false) 58 | ); 59 | } 60 | 61 | if (isMap(typeLiteral)) { 62 | String tagType = extractTagType(typeLiteral); 63 | String keyType = extractKeyType(typeLiteral); 64 | String valType = extractValueType(typeLiteral); 65 | 66 | return TypeBuilder.map( 67 | canonicalNameFactory.build(tagType), 68 | build(keyType, false), 69 | build(valType, false) 70 | ); 71 | } 72 | 73 | if (isPrimitive || typeLiteral.equals("char[]")) { 74 | return TypeBuilder.primitive(CanonicalName.primitive(typeLiteral)); 75 | } 76 | 77 | if (isArray(typeLiteral)) { 78 | String valType = extractArrayType(typeLiteral); 79 | return TypeBuilder.list( 80 | CanonicalName.fromString(List.class.getName()), 81 | build(valType, PRIMITIVES.contains(valType)) 82 | ); 83 | } 84 | 85 | return TypeBuilder.object(canonicalNameFactory.build(typeLiteral)); 86 | } 87 | 88 | private static boolean isList(String typeLiteral) { 89 | return typeLiteral.startsWith("List<") && typeLiteral.endsWith(">") 90 | || typeLiteral.startsWith("Collection<") && typeLiteral.endsWith(">"); 91 | } 92 | 93 | private static boolean isSet(String typeLiteral) { 94 | return typeLiteral.startsWith("Set<") && typeLiteral.endsWith(">"); 95 | } 96 | 97 | private static boolean isMap(String typeLiteral) { 98 | return typeLiteral.startsWith("Map<") && typeLiteral.endsWith(">"); 99 | } 100 | 101 | private static boolean isArray(String typeLiteral) { 102 | return typeLiteral.endsWith("[]"); 103 | } 104 | 105 | private static String extractTagType(String typeLiteral) { 106 | return typeLiteral.substring(0, typeLiteral.indexOf("<")); 107 | } 108 | 109 | private static String extractType(String typeLiteral) { 110 | return typeLiteral.substring(typeLiteral.indexOf("<") + 1, typeLiteral.length() - 1); 111 | } 112 | 113 | private static String extractKeyType(String typeLiteral) { 114 | int index = typeLiteral.indexOf(","); 115 | return typeLiteral.substring(4, index); 116 | } 117 | 118 | private static String extractValueType(String typeLiteral) { 119 | int index = typeLiteral.indexOf(","); 120 | return typeLiteral.substring(index + 1, typeLiteral.length() - 1).trim(); 121 | } 122 | 123 | private static String extractArrayType(String typeLiteral) { 124 | return typeLiteral.substring(0, typeLiteral.lastIndexOf("[]")); 125 | } 126 | 127 | } 128 | 129 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/reader/FileReader.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.reader; 2 | 3 | import static java.util.Optional.of; 4 | 5 | import com.github.havardh.javaflow.exceptions.ExitException; 6 | import com.github.havardh.javaflow.exceptions.ExitException.ErrorCode; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.nio.file.Paths; 11 | import java.util.Optional; 12 | 13 | /** 14 | * Reader to read a single file into a string 15 | */ 16 | public class FileReader { 17 | 18 | /** 19 | * Reads a single file into an Optional {@code String}. 20 | * 21 | * @param filename the name of the file to read 22 | * @return an Optional {@code String} with the file content 23 | */ 24 | public Optional read(String filename) { 25 | try { 26 | return of(new String(Files.readAllBytes(Paths.get(filename)))); 27 | } catch (IOException e) { 28 | throw new ExitException(ErrorCode.COULD_NOT_READ_FILE, e); 29 | } 30 | } 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/resolver/FileResolver.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.resolver; 2 | 3 | import com.github.havardh.javaflow.exceptions.ExitException; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.*; 7 | import java.nio.file.attribute.BasicFileAttributes; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | /** 14 | * Resolver to resolve a single pattern into a list of matching files 15 | */ 16 | public class FileResolver { 17 | 18 | /** 19 | * Reads a single pattern into a list of matching files 20 | * The patterns can either be a relative or absolute path 21 | * to a file or directory. Or a relative or absolute glob. 22 | * All patterns are relative to the working directory of 23 | * the process. 24 | * 25 | * @param pattern the file, directory or glob pattern 26 | * @return a list of paths matching the pattern 27 | */ 28 | public List resolve(String pattern) { 29 | try { 30 | Path path = Paths.get(".").resolve(pattern); 31 | if (Files.isRegularFile(path)) { 32 | return normalize(Collections.singletonList(path)); 33 | } else if (Files.isDirectory(path)) { 34 | return normalize(ofDirectory(path)); 35 | } else { 36 | return normalize(ofPattern(pattern)); 37 | } 38 | } catch (IOException e) { 39 | throw new ExitException(ExitException.ErrorCode.COULD_NOT_RESOLVE_PATTERN, e); 40 | } 41 | } 42 | 43 | private static List ofDirectory(Path path) throws IOException { 44 | FileVisitor visitor = FileVisitor.matchAllFiles(); 45 | Files.walkFileTree(Paths.get(".").resolve(path), visitor); 46 | return visitor.getPaths(); 47 | } 48 | 49 | private static List ofPattern(String pattern) throws IOException { 50 | FileVisitor visitor = FileVisitor.matchingGlob(pattern); 51 | Files.walkFileTree(Paths.get("."), visitor); 52 | return visitor.getPaths(); 53 | } 54 | 55 | private static List normalize(List paths) { 56 | return paths.stream().map(Path::normalize).collect(Collectors.toList()); 57 | } 58 | 59 | private static final class FileVisitor extends SimpleFileVisitor { 60 | 61 | private final PathMatcher pathMatcher; 62 | private final List paths = new ArrayList<>(); 63 | 64 | private FileVisitor() { 65 | this.pathMatcher = (p) -> true; 66 | } 67 | 68 | private FileVisitor(String pattern) { 69 | this.pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern); 70 | } 71 | 72 | static FileVisitor matchAllFiles() { 73 | return new FileVisitor(); 74 | } 75 | 76 | static FileVisitor matchingGlob(String glob) { 77 | return new FileVisitor(glob); 78 | } 79 | 80 | List getPaths() { 81 | return paths; 82 | } 83 | 84 | 85 | @Override 86 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { 87 | if (attrs.isRegularFile()) { 88 | Path normalized = file.normalize(); 89 | if (pathMatcher.matches(normalized) || pathMatcher.matches(normalized.toAbsolutePath())) { 90 | paths.add(file); 91 | } 92 | } 93 | 94 | return FileVisitResult.CONTINUE; 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/transform/InheritanceTransformer.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.transform; 2 | 3 | import static java.util.function.Function.identity; 4 | import static java.util.stream.Collectors.toList; 5 | import static java.util.stream.Collectors.toMap; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.function.Consumer; 10 | 11 | import com.github.havardh.javaflow.ast.Class; 12 | import com.github.havardh.javaflow.ast.Parent; 13 | import com.github.havardh.javaflow.ast.Type; 14 | 15 | /** 16 | * Transformer to resolve inheritance relations 17 | */ 18 | public class InheritanceTransformer implements Transformer { 19 | 20 | /** 21 | * Resolve inheritance 22 | * 23 | * Updates all {@code Class}es which inherits another type 24 | * with a parent reference to the parent {@code Class}. 25 | * This reference is used when the {@code Class} lists 26 | * its fields with the {@code Class#getFields} method. 27 | * 28 | * @param types the list to transform 29 | */ 30 | @Override 31 | public void transform(List types) { 32 | 33 | List classes = types.stream() 34 | .filter(type -> type instanceof Class) 35 | .map(type -> (Class) type) 36 | .collect(toList()); 37 | 38 | Map typeMap = classes.stream() 39 | .collect(toMap(Class::toString, identity())); 40 | 41 | classes.forEach(setParentReference(typeMap)); 42 | } 43 | 44 | private static Consumer setParentReference(Map typeMap) { 45 | return aClass -> aClass.getParent().ifPresent(updateParentReference(typeMap)); 46 | } 47 | 48 | private static Consumer updateParentReference(Map typeMap) { 49 | return parent -> parent.setReference(typeMap.get(parent.getCanonicalName().toString())); 50 | } 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/transform/SortedTypeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.transform; 2 | 3 | import java.util.List; 4 | 5 | import com.github.havardh.javaflow.ast.Type; 6 | 7 | /** 8 | * Transformer to sort a {@code Type} list alphabetically based 9 | * on type names. 10 | */ 11 | public class SortedTypeTransformer implements Transformer { 12 | 13 | /** 14 | * Sorts the {@code Type} list alphabetically on name 15 | * 16 | * @param types the list to sort 17 | */ 18 | @Override 19 | public void transform(List types) { 20 | types.sort((o1, o2) -> { 21 | String name1 = o1.getCanonicalName().toString(); 22 | String name2 = o2.getCanonicalName().toString(); 23 | 24 | return name1.compareToIgnoreCase(name2); 25 | }); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/transform/Transformer.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.transform; 2 | 3 | import java.util.List; 4 | 5 | import com.github.havardh.javaflow.ast.Type; 6 | 7 | /** 8 | * Interface to transformers of {@code Type} lists. 9 | */ 10 | public interface Transformer { 11 | 12 | /** 13 | * Transforms the {@code Type} list in-place. 14 | * 15 | * @param types the list to transform 16 | */ 17 | void transform(List types); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/verifier/ClassGetterNamingVerifier.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.verifier; 2 | 3 | import static java.lang.String.format; 4 | import static java.util.Collections.singletonList; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import com.github.havardh.javaflow.ast.Class; 10 | import com.github.havardh.javaflow.ast.Field; 11 | import com.github.havardh.javaflow.ast.Method; 12 | import com.github.havardh.javaflow.ast.Type; 13 | import com.github.havardh.javaflow.exceptions.AggregatedException; 14 | import com.github.havardh.javaflow.exceptions.FieldGettersMismatchException; 15 | 16 | public class ClassGetterNamingVerifier implements Verifier { 17 | 18 | /** 19 | * Verifies that the given types all have: 20 | * - the same number of fields and getters, 21 | * - the same type in the field definitions as the return type in the corresponding getter and 22 | * - their getters for the corresponding fields with the same name, prefixed with `get` or `is`. 23 | * 24 | * @param types list of {@code Type} to verify. 25 | */ 26 | @Override 27 | public void verify(List types) { 28 | List exceptions = new ArrayList<>(); 29 | for (Type type : types) { 30 | if (type instanceof Class) { 31 | exceptions.addAll(validate((Class) type)); 32 | } 33 | } 34 | 35 | if (!exceptions.isEmpty()) { 36 | throw new AggregatedException("Class getter naming validation failed with following errors:\n", exceptions, true); 37 | } 38 | } 39 | 40 | private List validate(Class classToValidate) { 41 | List getters = classToValidate.getGetters(); 42 | List fields = classToValidate.getFields(); 43 | if (getters.size() != fields.size()) { 44 | return singletonList(new FieldGettersMismatchException(classToValidate.getCanonicalName(), format( 45 | "Number of getters and fields is not the same.\n" + 46 | "Fields in model: %s\n" + 47 | "Getters in model: %s", 48 | fields, 49 | getters 50 | ))); 51 | } 52 | List exceptions = new ArrayList<>(); 53 | for (Method getter : getters) { 54 | try { 55 | Field correspondingField = findFieldByGetter(classToValidate, fields, getter); 56 | 57 | if (!correspondingField.getType().equals(getter.getType())) { 58 | throw new FieldGettersMismatchException( 59 | classToValidate.getCanonicalName(), 60 | format( 61 | "Type of getter %s (%s) does not correspond to field %s (%s)", 62 | getter.getName(), 63 | getter.getType(), 64 | correspondingField.getName(), 65 | correspondingField.getType() 66 | ) 67 | ); 68 | } 69 | } catch (FieldGettersMismatchException e) { 70 | exceptions.add(e); 71 | } 72 | } 73 | return exceptions; 74 | } 75 | 76 | private Field findFieldByGetter(Class classToValidate, List fields, Method getter) 77 | throws FieldGettersMismatchException { 78 | return fields.stream() 79 | .filter(field -> field.getName().equals(convertGetterNameToFieldName(getter.getName()))) 80 | .findFirst() 81 | .orElseThrow(() -> new FieldGettersMismatchException( 82 | classToValidate.getCanonicalName(), 83 | format("Name of getter %s does not correspond to any field name.", getter.getName()) 84 | )); 85 | } 86 | 87 | private static String convertGetterNameToFieldName(String getterName) { 88 | if (getterName.startsWith("get") && getterName.length() > 3) { 89 | return Character.toLowerCase(getterName.charAt(3)) + (getterName.length() > 4 ? getterName.substring(4) : ""); 90 | } 91 | 92 | if (getterName.startsWith("is") && getterName.length() > 2) { 93 | return Character.toLowerCase(getterName.charAt(2)) + (getterName.length() > 4 ? getterName.substring(3) : ""); 94 | } 95 | 96 | return getterName; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/verifier/MemberFieldsPresentVerifier.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.verifier; 2 | 3 | import static java.util.Collections.singletonList; 4 | import static java.util.stream.Collectors.toList; 5 | import static java.util.stream.Collectors.toSet; 6 | 7 | import static com.github.havardh.javaflow.phases.writer.flow.converter.definitions.Objects.isObject; 8 | import static com.github.havardh.javaflow.phases.writer.flow.converter.definitions.Primitives.isPrimitive; 9 | import static com.github.havardh.javaflow.util.Lists.concat; 10 | 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Set; 15 | 16 | import com.github.havardh.javaflow.ast.Class; 17 | import com.github.havardh.javaflow.ast.Field; 18 | import com.github.havardh.javaflow.ast.Type; 19 | import com.github.havardh.javaflow.exceptions.MissingTypeException; 20 | import com.github.havardh.javaflow.model.CanonicalName; 21 | import com.github.havardh.javaflow.model.TypeMap; 22 | 23 | public class MemberFieldsPresentVerifier implements Verifier { 24 | 25 | private final TypeMap customTypes; 26 | 27 | /** 28 | * Create a {@code MemberFieldPresentVerifier} for the given 29 | * {@code TypeMap}. 30 | * 31 | * @param customTypes the {@code TypeMap} to verify based on 32 | */ 33 | public MemberFieldsPresentVerifier(TypeMap customTypes) { 34 | this.customTypes = customTypes; 35 | } 36 | 37 | /** 38 | * Verifies that all types used in the types converted are either: 39 | * - built-in java types, 40 | * - overridden by custom type substitution, or 41 | * - included as a type being converted. 42 | * 43 | * This ensures that the resulting flow file will not contain any references to missing types. 44 | * 45 | * A missing type is found when a type is referenced which is not 46 | * in the given set of types of in the {@code TypeMap} of the verifier 47 | * If a any missing types are discovered a {@code MissingTypeException} 48 | * is thrown containing the complete list of missing types. 49 | * 50 | * @param types list of {@code Type} to verify 51 | * @throws MissingTypeException when a missing type is found 52 | */ 53 | @Override 54 | public void verify(List types) { 55 | Set nameSet = types.stream() 56 | .map(Type::getCanonicalName) 57 | .collect(toSet()); 58 | 59 | Map> missingTypes = new HashMap<>(); 60 | for (Type type : types.stream().filter(t -> t instanceof Class).collect(toList())) { 61 | ((Class) type).getFields().stream() 62 | .filter(field -> !nameSet.contains(field.getType().getCanonicalName())) 63 | .filter(field -> !isObject(field.getType().toString())) 64 | .filter(field -> !isPrimitive(field.getType().toString())) 65 | .filter(field -> !customTypes.containsKey(field.getType().toString())) 66 | .forEach(field -> missingTypes.compute(type, (ignored, fields) -> fields == null 67 | ? singletonList(field) 68 | : concat(fields, singletonList(field)))); 69 | } 70 | 71 | 72 | if (!missingTypes.isEmpty()) { 73 | throw new MissingTypeException(missingTypes); 74 | } 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/verifier/Verifier.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.verifier; 2 | 3 | import java.util.List; 4 | 5 | import com.github.havardh.javaflow.ast.Type; 6 | 7 | /** 8 | * Interface for verifiers on list of {@code Type}. 9 | * 10 | * The verifiers reads all the internal {@code Type} classes and runs 11 | * static verification to ensure that the types are internally consistent 12 | * to provide more detailed errors if they are not. 13 | */ 14 | public interface Verifier { 15 | 16 | /** 17 | * Verifies that the list of {@code Type} are internally 18 | * consistent. If they are not the {@code Verifier} should 19 | * throw a {@code RuntimeException} to inform the user what 20 | * part of the input is non consistent. 21 | * 22 | * @param types list of {@code Type} to verify 23 | */ 24 | void verify(List types); 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/Writer.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Interface for writing an object to a {@code java.io.Writer}. 7 | * 8 | * @param the 9 | */ 10 | public interface Writer { 11 | 12 | /** 13 | * Writes the given parameter to the {@code writer}. 14 | * 15 | * @param t the object to write 16 | * @param writer the writer to write to 17 | * @throws IOException thrown when the writer throws an IOException 18 | */ 19 | void write(T t, java.io.Writer writer) throws IOException; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/ClassWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import java.io.IOException; 4 | 5 | import com.github.havardh.javaflow.ast.Class; 6 | import com.github.havardh.javaflow.ast.Field; 7 | import com.github.havardh.javaflow.phases.writer.Writer; 8 | import com.github.havardh.javaflow.phases.writer.flow.converter.Converter; 9 | 10 | /** 11 | * {@code Writer} for writing {@code Class} 12 | */ 13 | class ClassWriter implements Writer { 14 | 15 | private Writer fieldWriter; 16 | 17 | /** 18 | * Create a {@code Writer} for {@code Class} 19 | * 20 | * @param converter type converter to convert types from source language 21 | * flow types. 22 | */ 23 | public ClassWriter(Converter converter) { 24 | this.fieldWriter = new FieldDefinitionWriter(converter); 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public void write(Class aClass, java.io.Writer writer) throws IOException { 30 | writer.write("export type "); 31 | writer.write(aClass.getCanonicalName().getName()); 32 | writer.write(" = {"); 33 | writeFields(aClass, writer); 34 | writer.write("};"); 35 | } 36 | 37 | private void writeFields(Class aClass, java.io.Writer writer) throws IOException { 38 | if (aClass.getFields().size() > 0) { 39 | writer.write("\n"); 40 | } 41 | for (Field field : aClass.getFields()) { 42 | writer.write(" "); 43 | fieldWriter.write(field, writer); 44 | writer.write(",\n"); 45 | } 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/EnumWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import java.io.IOException; 4 | 5 | import com.github.havardh.javaflow.ast.Enum; 6 | import com.github.havardh.javaflow.phases.writer.Writer; 7 | 8 | /** 9 | * {@code Writer} for writing {@code Enum} 10 | */ 11 | class EnumWriter implements Writer { 12 | 13 | /** {@inheritDoc} */ 14 | @Override 15 | public void write(Enum anEnum, java.io.Writer writer) throws IOException { 16 | writer.write("export type "); 17 | writer.write(anEnum.getCanonicalName().getName()); 18 | writer.write(" ="); 19 | writeValues(anEnum, writer); 20 | writer.write(";"); 21 | } 22 | 23 | private void writeValues(Enum anEnum, java.io.Writer writer) throws IOException { 24 | for (String value : anEnum.getValues()) { 25 | writer.write("\n | \""); 26 | writer.write(value); 27 | writer.write("\""); 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/FieldDefinitionWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import java.io.IOException; 4 | 5 | import com.github.havardh.javaflow.ast.Field; 6 | import com.github.havardh.javaflow.ast.Type; 7 | import com.github.havardh.javaflow.phases.writer.Writer; 8 | import com.github.havardh.javaflow.phases.writer.flow.converter.Converter; 9 | 10 | /** 11 | * {@code Writer} for writing {@code Field} 12 | */ 13 | public class FieldDefinitionWriter implements Writer { 14 | 15 | private Writer typeWriter; 16 | 17 | /** 18 | * Create a {@code Writer} for {@code Field} 19 | * 20 | * @param converter type converter to convert types from source language 21 | * flow types. 22 | */ 23 | public FieldDefinitionWriter(Converter converter) { 24 | this.typeWriter = new TypeWriter(converter); 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public void write(Field field, java.io.Writer writer) throws IOException { 30 | writer.write(field.getName()); 31 | writer.write(": "); 32 | if (field.isNullable()) { 33 | writer.write("?"); 34 | } 35 | typeWriter.write(field.getType(), writer); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/FlowWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import java.io.IOException; 4 | 5 | import com.github.havardh.javaflow.ast.Class; 6 | import com.github.havardh.javaflow.ast.Enum; 7 | import com.github.havardh.javaflow.ast.Type; 8 | import com.github.havardh.javaflow.phases.writer.Writer; 9 | import com.github.havardh.javaflow.phases.writer.flow.converter.Converter; 10 | 11 | /** 12 | * {@code Writer} for writing flow types for {@code Type}. 13 | */ 14 | public class FlowWriter implements Writer { 15 | 16 | private final ClassWriter classWriter; 17 | private final EnumWriter enumWriter; 18 | 19 | /** 20 | * Create a flow type {@code Writer} 21 | * 22 | * @param converter type converter to convert types from source language 23 | * flow types. 24 | */ 25 | public FlowWriter(Converter converter) { 26 | classWriter = new ClassWriter(converter); 27 | enumWriter = new EnumWriter(); 28 | } 29 | 30 | /** {@inheritDoc} */ 31 | @Override 32 | public void write(Type type, java.io.Writer writer) throws IOException { 33 | if (type instanceof Class) { 34 | classWriter.write((Class) type, writer); 35 | } else if (type instanceof Enum) { 36 | enumWriter.write((Enum) type, writer); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/TypeWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import java.io.IOException; 4 | 5 | import com.github.havardh.javaflow.ast.List; 6 | import com.github.havardh.javaflow.ast.Map; 7 | import com.github.havardh.javaflow.ast.Type; 8 | import com.github.havardh.javaflow.phases.writer.Writer; 9 | import com.github.havardh.javaflow.phases.writer.flow.converter.Converter; 10 | 11 | /** 12 | * {@code Writer} for writing {@code Type}. 13 | */ 14 | public class TypeWriter implements Writer { 15 | 16 | private final Converter converter; 17 | 18 | /** 19 | * Create a {@code Writer} for {@code Type} 20 | * 21 | * @param converter type converter to convert types from source language 22 | * flow types. 23 | */ 24 | public TypeWriter(Converter converter) { 25 | this.converter = converter; 26 | } 27 | 28 | /** {@inheritDoc} */ 29 | @Override 30 | public void write(Type type, java.io.Writer writer) throws IOException { 31 | if (type instanceof Map) { 32 | write((Map) type, writer); 33 | } else if (type instanceof List) { 34 | write((List) type, writer); 35 | } else { 36 | writer.write(converter.convert(type.getCanonicalName())); 37 | } 38 | } 39 | 40 | private void write(Map map, java.io.Writer writer) throws IOException { 41 | writer.write("{[key: "); 42 | write(map.getKeyType(), writer); 43 | writer.write("]: "); 44 | write(map.getValueType(), writer); 45 | writer.write("}"); 46 | } 47 | 48 | private void write(List list, java.io.Writer writer) throws IOException { 49 | writer.write(converter.convert(list.getCanonicalName())); 50 | writer.write("<"); 51 | write(list.getType(), writer); 52 | writer.write(">"); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/converter/Converter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow.converter; 2 | 3 | import com.github.havardh.javaflow.model.CanonicalName; 4 | 5 | /** 6 | * Interface for type converters. The converter 7 | * converts types from the source language to the 8 | * target language. 9 | */ 10 | public interface Converter { 11 | 12 | /** 13 | * Converts a {@code CanonicalName} in the source language 14 | * to a type in the target language. 15 | * 16 | * @param name a {@code CanonicalName} in the source language 17 | * @return the {@code String} name in the target language 18 | */ 19 | String convert(CanonicalName name); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/converter/JavaFlowConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow.converter; 2 | 3 | import com.github.havardh.javaflow.phases.writer.flow.converter.definitions.Objects; 4 | import com.github.havardh.javaflow.phases.writer.flow.converter.definitions.Primitives; 5 | import com.github.havardh.javaflow.model.CanonicalName; 6 | import com.github.havardh.javaflow.model.TypeMap; 7 | 8 | import java.util.function.Supplier; 9 | 10 | /** 11 | * Type {@code Converter} from Java to flowtypes. 12 | */ 13 | public final class JavaFlowConverter implements Converter { 14 | 15 | private final TypeMap customTypeMap; 16 | 17 | /** 18 | * Create an empty {@code JavaFlowConverter}. 19 | */ 20 | public JavaFlowConverter() { 21 | this.customTypeMap = TypeMap.emptyTypeMap(); 22 | } 23 | 24 | /** 25 | * Create a {@code JavaFlowConverter} for a {@code TypeMap} 26 | * 27 | * @param typeMap the {@code TypeMap} for the converter 28 | */ 29 | public JavaFlowConverter(TypeMap typeMap) { 30 | this.customTypeMap = typeMap; 31 | } 32 | 33 | /** 34 | * Convert a @{code CanonicalName} Java name to a flow type. 35 | * 36 | * @param canonicalName the name to convert 37 | * @return the flow type for the {@code CanonicalName} 38 | */ 39 | public String convert(CanonicalName canonicalName) { 40 | String name = canonicalName.getName(); 41 | String fullName = canonicalName.toString(); 42 | 43 | return firstNonNull( 44 | () -> customTypeMap.get(fullName), 45 | () -> customTypeMap.get(name), 46 | () -> get(fullName), 47 | () -> name 48 | ); 49 | } 50 | 51 | private static String get(String name) { 52 | if (Objects.isObject(name)) { 53 | return Objects.get(name); 54 | } 55 | 56 | if (Primitives.isPrimitive(name)) { 57 | return Primitives.get(name); 58 | } 59 | 60 | return null; 61 | } 62 | 63 | private static T firstNonNull(Supplier... suppliers) { 64 | for (Supplier supplier : suppliers) { 65 | T value = supplier.get(); 66 | if (value != null) { 67 | return value; 68 | } 69 | } 70 | return null; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/converter/definitions/Objects.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow.converter.definitions; 2 | 3 | import java.util.Map; 4 | 5 | import com.github.havardh.javaflow.util.Maps; 6 | 7 | /** 8 | * Static utility for converting fro java built-in object types to flow types 9 | */ 10 | public class Objects { 11 | 12 | public static Map OBJECTS = Maps.collect( 13 | Maps.entry("char[]", "string"), 14 | 15 | Maps.entry("java.util.Date", "string"), 16 | Maps.entry("java.util.Map", "Map"), 17 | Maps.entry("java.util.List", "Array"), 18 | Maps.entry("java.util.Collection", "Array"), 19 | Maps.entry("java.util.Set", "Array"), 20 | 21 | Maps.entry("java.lang.Boolean", "boolean"), 22 | Maps.entry("java.lang.Byte", "number"), 23 | Maps.entry("java.lang.Character", "string"), 24 | Maps.entry("java.lang.Double", "number"), 25 | Maps.entry("java.lang.Float", "number"), 26 | Maps.entry("java.lang.Integer", "number"), 27 | Maps.entry("java.lang.Long", "number"), 28 | Maps.entry("java.lang.Short", "number"), 29 | Maps.entry("java.lang.String", "string") 30 | ); 31 | 32 | /** 33 | * Check if a name is a java built-in object 34 | * 35 | * @param name the name to check 36 | * @return true if the name is a built-in object 37 | */ 38 | public static boolean isObject(String name) { 39 | return OBJECTS.containsKey(name); 40 | } 41 | 42 | /** 43 | * Get the flow type of a java built-in type 44 | * 45 | * @param name the java built-in type 46 | * @return the flow type 47 | */ 48 | public static String get(String name) { 49 | return OBJECTS.get(name); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/phases/writer/flow/converter/definitions/Primitives.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow.converter.definitions; 2 | 3 | import java.util.Map; 4 | 5 | import com.github.havardh.javaflow.util.Maps; 6 | 7 | /** 8 | * Static utility for converting fro java primitive types to flow types 9 | */ 10 | public class Primitives { 11 | 12 | public static Map PRIMITIVES = Maps.collect( 13 | Maps.entry("byte", "number"), 14 | Maps.entry("short", "number"), 15 | Maps.entry("int", "number"), 16 | Maps.entry("long", "number"), 17 | Maps.entry("float", "number"), 18 | Maps.entry("double", "number"), 19 | Maps.entry("boolean", "boolean"), 20 | Maps.entry("char", "string") 21 | ); 22 | 23 | /** 24 | * Check if a name is a java primitive 25 | * 26 | * @param name the name to check 27 | * @return true if the name is a primitive 28 | */ 29 | public static boolean isPrimitive(String name) { 30 | return PRIMITIVES.containsKey(name); 31 | } 32 | 33 | /** 34 | * Get the flow type of a java primitive 35 | * 36 | * @param name the java primitive 37 | * @return the flow type 38 | */ 39 | public static String get(String name) { 40 | return PRIMITIVES.get(name); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/util/App.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.util; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Properties; 6 | 7 | public final class App { 8 | 9 | private static final Properties properties = new Properties(); 10 | 11 | static { 12 | try (InputStream inputStream = App.class.getResourceAsStream("/version.properties")) { 13 | 14 | properties.load(inputStream); 15 | 16 | } catch (IOException e) { 17 | e.printStackTrace(); 18 | } 19 | } 20 | 21 | public static String version() { 22 | return properties.getProperty("version", "no-version"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/util/Lists.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.util; 2 | 3 | import static java.util.stream.Collectors.toList; 4 | 5 | import java.util.List; 6 | import java.util.stream.Stream; 7 | 8 | /** 9 | * Static utilities for {@code List} 10 | */ 11 | public final class Lists { 12 | private Lists() { 13 | } 14 | 15 | /** 16 | * Concatenates two lists of type {@code T} 17 | * 18 | * @param a the first list 19 | * @param b the second list 20 | * @param the type of the lists 21 | * @return the concatenation of {@code a} and {@code b} 22 | */ 23 | public static List concat(List a, List b) { 24 | return Stream.concat(a.stream(), b.stream()).collect(toList()); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/havardh/javaflow/util/Maps.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.util; 2 | 3 | import static java.util.Arrays.stream; 4 | 5 | import java.util.AbstractMap; 6 | import java.util.Collections; 7 | import java.util.Map; 8 | import java.util.stream.Collector; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * Static utilities for {@code Map} 13 | */ 14 | public class Maps { 15 | 16 | /** 17 | * Factory method for creating a AbstractMap.SimpleEntry 18 | * 19 | * @param key key of type {@code K} 20 | * @param value value of type {@code V} 21 | * @param type for {@code key} 22 | * @param type for {@code value} 23 | * @return a SimpleEntry 24 | */ 25 | public static Map.Entry entry(K key, V value) { 26 | return new AbstractMap.SimpleEntry<>(key, value); 27 | } 28 | 29 | /** 30 | * Collect a list of entries to an unmodifiable map 31 | * 32 | * @param entries list of map entries 33 | * @param type of map key 34 | * @param type of map value 35 | * @return an unmodifiable map containing the entries 36 | */ 37 | public static Map collect(Map.Entry... entries) { 38 | return Collections.unmodifiableMap(stream(entries).collect(entriesToMap())); 39 | } 40 | 41 | private static Collector, ?, Map> entriesToMap() { 42 | return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/main/resources/version.properties: -------------------------------------------------------------------------------- 1 | /* this file is written to by gradle build */ -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/ExecutionIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow; 2 | 3 | import static com.github.havardh.javaflow.phases.parser.java.JavaParserFlags.defaultFlags; 4 | import static java.util.Arrays.asList; 5 | import static java.util.Collections.emptyList; 6 | 7 | import static org.hamcrest.MatcherAssert.assertThat; 8 | import static org.hamcrest.Matchers.hasSize; 9 | import static org.junit.jupiter.api.Assertions.fail; 10 | 11 | import static com.github.havardh.javaflow.testutil.Assertions.assertStringEqual; 12 | 13 | import com.github.havardh.javaflow.model.TypeMap; 14 | import com.github.havardh.javaflow.phases.resolver.FileResolver; 15 | import org.junit.jupiter.api.BeforeEach; 16 | import org.junit.jupiter.api.Test; 17 | 18 | import com.github.havardh.javaflow.exceptions.AggregatedException; 19 | import com.github.havardh.javaflow.exceptions.MissingTypeException; 20 | import com.github.havardh.javaflow.phases.parser.java.JavaParser; 21 | import com.github.havardh.javaflow.phases.reader.FileReader; 22 | import com.github.havardh.javaflow.phases.transform.InheritanceTransformer; 23 | import com.github.havardh.javaflow.phases.transform.SortedTypeTransformer; 24 | import com.github.havardh.javaflow.phases.verifier.ClassGetterNamingVerifier; 25 | import com.github.havardh.javaflow.phases.verifier.MemberFieldsPresentVerifier; 26 | import com.github.havardh.javaflow.phases.writer.flow.FlowWriter; 27 | import com.github.havardh.javaflow.phases.writer.flow.converter.JavaFlowConverter; 28 | 29 | public class ExecutionIntegrationTest { 30 | 31 | private static final String BASE_PATH = "src/test/java/com/github/havardh/javaflow/model/"; 32 | 33 | private Execution execution; 34 | 35 | @BeforeEach 36 | public void setup() { 37 | TypeMap typeMap = TypeMap.of("?", "any"); 38 | this.execution = new Execution( 39 | new FileResolver(), 40 | new FileReader(), 41 | new JavaParser(defaultFlags()), 42 | asList( 43 | new InheritanceTransformer(), 44 | new SortedTypeTransformer() 45 | ), 46 | asList(new MemberFieldsPresentVerifier(typeMap), new ClassGetterNamingVerifier()), 47 | new FlowWriter(new JavaFlowConverter(typeMap)), 48 | emptyList() 49 | ); 50 | } 51 | 52 | @Test 53 | public void shouldParseModel() { 54 | String flowCode = execution.run(BASE_PATH + "Model.java"); 55 | 56 | assertStringEqual( 57 | flowCode, 58 | "export type Model = {", 59 | " yolo: string,", 60 | "};" 61 | ); 62 | } 63 | 64 | @Test 65 | public void shouldParseEnum() { 66 | String flowCode = execution.run(BASE_PATH + "Enumeration.java"); 67 | 68 | assertStringEqual( 69 | flowCode, 70 | "export type Enumeration =", 71 | " | \"ONE\"", 72 | " | \"TWO\";" 73 | ); 74 | } 75 | 76 | @Test 77 | public void shouldParseModelWithFieldComments() { 78 | String flowCode = execution.run(BASE_PATH + "ModelWithFieldComments.java"); 79 | 80 | assertStringEqual( 81 | flowCode, 82 | "export type ModelWithFieldComments = {", 83 | " field1: string,", 84 | " field2: string,", 85 | " field3: string,", 86 | " field4: string,", 87 | "};" 88 | ); 89 | } 90 | 91 | @Test 92 | public void shouldParseModelWithList() { 93 | String flowCode = execution.run(BASE_PATH + "ModelWithList.java"); 94 | 95 | assertStringEqual( 96 | flowCode, 97 | "export type ModelWithList = {", 98 | " words: Array,", 99 | "};" 100 | ); 101 | } 102 | 103 | @Test 104 | public void shouldParseModelWithMap() { 105 | String flowCode = execution.run(BASE_PATH + "ModelWithMap.java"); 106 | 107 | assertStringEqual( 108 | flowCode, 109 | "export type ModelWithMap = {", 110 | " field: {[key: string]: number},", 111 | "};" 112 | ); 113 | } 114 | 115 | @Test 116 | public void shouldParseModelWithMapGenericValue() { 117 | String flowCode = execution.run(BASE_PATH + "ModelWithMapGenericValue.java"); 118 | 119 | assertStringEqual( 120 | flowCode, 121 | "export type ModelWithMapGenericValue = {", 122 | " field: {[key: string]: Array},", 123 | " multipleNestedField: {[key: string]: Array>},", 124 | "};" 125 | ); 126 | } 127 | 128 | @Test 129 | public void shouldParseModelWithGenericMapKey() { 130 | String flowCode = execution.run(BASE_PATH + "ModelWithGenericMapKey.java"); 131 | 132 | assertStringEqual( 133 | flowCode, 134 | "export type ModelWithGenericMapKey = {", 135 | " field: {[key: Array]: number},", 136 | "};" 137 | ); 138 | } 139 | 140 | @Test 141 | public void shouldParseModelWithCollection() { 142 | String flowCode = execution.run(BASE_PATH + "ModelWithCollection.java"); 143 | 144 | assertStringEqual( 145 | flowCode, 146 | "export type ModelWithCollection = {", 147 | " strings: Array,", 148 | "};" 149 | ); 150 | } 151 | 152 | @Test 153 | public void shouldParseModelWithCharArray() { 154 | String flowCode = execution.run(BASE_PATH + "ModelWithCharArray.java"); 155 | 156 | assertStringEqual( 157 | flowCode, 158 | "export type ModelWithCharArray = {", 159 | " field: string,", 160 | "};" 161 | ); 162 | } 163 | 164 | @Test 165 | public void shouldParseModelWithArrays() { 166 | String flowCode = execution.run(BASE_PATH + "ModelWithGenericArrays.java"); 167 | 168 | assertStringEqual( 169 | flowCode, 170 | "export type ModelWithGenericArrays = {", 171 | " field1: Array,", 172 | " field2: Array,", 173 | "};" 174 | ); 175 | } 176 | 177 | @Test 178 | public void shouldParseModelWithSet() { 179 | String flowCode = execution.run(BASE_PATH + "ModelWithSet.java"); 180 | 181 | assertStringEqual( 182 | flowCode, 183 | "export type ModelWithSet = {", 184 | " strings: Array,", 185 | "};" 186 | ); 187 | } 188 | 189 | @Test 190 | public void shouldParseModelWithMember() { 191 | String flowCode = execution.run( 192 | BASE_PATH + "Wrapper.java", 193 | BASE_PATH + "Member.java", 194 | BASE_PATH + "packaged/PackagedMember.java" 195 | ); 196 | 197 | assertStringEqual( 198 | flowCode, 199 | "export type Member = {};", 200 | "", 201 | "export type PackagedMember = {};", 202 | "", 203 | "export type Wrapper = {", 204 | " member: Member,", 205 | " packagedMember: PackagedMember,", 206 | "};" 207 | ); 208 | } 209 | 210 | @Test 211 | public void shouldParseModelWithNullableField() { 212 | String flowCode = execution.run( 213 | BASE_PATH + "ModelWithNullableField.java" 214 | ); 215 | 216 | assertStringEqual( 217 | flowCode, 218 | "export type ModelWithNullableField = {", 219 | " field: ?string,", 220 | "};" 221 | ); 222 | } 223 | 224 | @Test 225 | public void shouldParseModelWithAnonymousClass() { 226 | String flowCode = execution.run( 227 | BASE_PATH + "ModelWithAnonymousClass.java", 228 | BASE_PATH + "Member.java" 229 | ); 230 | 231 | assertStringEqual( 232 | flowCode, 233 | "export type Member = {};", 234 | "", 235 | "export type ModelWithAnonymousClass = {", 236 | " field: Member,", 237 | "};" 238 | ); 239 | } 240 | 241 | @Test 242 | public void shouldParseModelWithInnerClasses() { 243 | String flowCode = execution.run(BASE_PATH + "ModelWithInnerClasses.java"); 244 | 245 | assertStringEqual( 246 | flowCode, 247 | "export type ModelWithInnerClasses = {", 248 | " child1: Child1,", 249 | " child2: Child2,", 250 | " child3: Child3,", 251 | " child4: Child4,", 252 | "};", 253 | "", 254 | "export type Child1 = {};", 255 | "", 256 | "export type Child2 = {", 257 | " field1: number,", 258 | "};", 259 | "", 260 | "export type Child3 = {", 261 | " field1: string,", 262 | "};", 263 | "", 264 | "export type Child4 = {", 265 | " field1: string,", 266 | "};" 267 | ); 268 | } 269 | 270 | @Test 271 | public void shouldParseModelWithPrimitive() { 272 | String flowCode = execution.run(BASE_PATH + "ModelWithPrimitive.java"); 273 | 274 | assertStringEqual( 275 | flowCode, 276 | "export type ModelWithPrimitive = {", 277 | " byteField: number,", 278 | " shortField: number,", 279 | " intField: number,", 280 | " longField: number,", 281 | " floatField: number,", 282 | " doubleField: number,", 283 | " booleanField: boolean,", 284 | " booleanField2: boolean,", 285 | " charField: string,", 286 | "};" 287 | ); 288 | } 289 | 290 | @Test 291 | public void shouldParseModelWithJavaLangObjects() { 292 | String flowCode = execution.run(BASE_PATH + "ModelWithJavaLangObjects.java"); 293 | 294 | assertStringEqual( 295 | flowCode, 296 | "export type ModelWithJavaLangObjects = {", 297 | " booleanField: boolean,", 298 | " booleanField2: boolean,", 299 | " byteField: number,", 300 | " characterField: string,", 301 | " doubleField: number,", 302 | " floatField: number,", 303 | " integerField: number,", 304 | " longField: number,", 305 | " shortField: number,", 306 | " stringField: string,", 307 | "};" 308 | ); 309 | } 310 | 311 | @Test 312 | public void shouldParseModelWithStaticFields() { 313 | String flowCode = execution.run(BASE_PATH + "ModelWithStaticFields.java"); 314 | 315 | assertStringEqual( 316 | flowCode, 317 | "export type ModelWithStaticFields = {};" 318 | ); 319 | } 320 | 321 | @Test 322 | public void shouldParseModelWithWildcardTypeParams() { 323 | String flowCode = execution.run(BASE_PATH + "ModelWithWildcardTypeParams.java"); 324 | 325 | assertStringEqual( 326 | flowCode, 327 | "export type ModelWithWildcardTypeParams = {", 328 | " wildcardList: Array,", 329 | " wildcardMap: {[key: any]: Array},", 330 | "};" 331 | ); 332 | } 333 | 334 | @Test 335 | public void shouldThrowExceptionWhenFieldIsNotFound() { 336 | try { 337 | execution.run(BASE_PATH + "Wrapper.java"); 338 | fail("Should fail when member types are not found"); 339 | } catch (AggregatedException e) { 340 | MissingTypeException missingTypeException = (MissingTypeException) e.getExceptions().get(0); 341 | assertThat(missingTypeException.getTypes().entrySet(), hasSize(1)); 342 | } 343 | } 344 | 345 | @Test 346 | public void shouldThrowExceptionWhenGetterDoesNotHaveMatchingField() { 347 | try { 348 | execution.run(BASE_PATH + "ModelWithNotMatchingGetter.java"); 349 | fail("Should fail when getter does not have matching field"); 350 | } catch (AggregatedException e) { 351 | assertThat(e.getExceptions(), hasSize(1)); 352 | assertStringEqual(e.getMessage(), "Verification failed:", 353 | "", 354 | "Class getter naming validation failed with following errors:", 355 | "", 356 | "1) Model {com.github.havardh.javaflow.model.ModelWithNotMatchingGetter} is not a pure DTO.", 357 | "Name of getter getStringFields does not correspond to any field name." 358 | ); 359 | } 360 | } 361 | 362 | @Test 363 | public void shouldThrowExceptionWhenBooleanGetterDoesNotHaveMatchingField() { 364 | try { 365 | execution.run(BASE_PATH + "ModelWithNotMatchingBooleanGetter.java"); 366 | fail("Should fail when boolean getter does not have matching field"); 367 | } catch (AggregatedException e) { 368 | assertThat(e.getExceptions(), hasSize(1)); 369 | assertStringEqual(e.getMessage(), "Verification failed:", 370 | "", 371 | "Class getter naming validation failed with following errors:", 372 | "", 373 | "1) Model {com.github.havardh.javaflow.model.ModelWithNotMatchingBooleanGetter} is not a pure DTO.", 374 | "Name of getter isBooleanFields does not correspond to any field name." 375 | ); 376 | } 377 | } 378 | 379 | @Test 380 | public void shouldThrowExceptionWhenDifferentNumberOfGettersAndFields() { 381 | try { 382 | execution.run(BASE_PATH + "ModelWithoutGetters.java"); 383 | fail("Should fail when model has no getters"); 384 | } catch (AggregatedException e) { 385 | assertThat(e.getExceptions(), hasSize(1)); 386 | assertStringEqual(e.getMessage(), "Verification failed:", 387 | "", 388 | "Class getter naming validation failed with following errors:", 389 | "", 390 | "1) Model {com.github.havardh.javaflow.model.ModelWithoutGetters} is not a pure DTO.", 391 | "Number of getters and fields is not the same.", 392 | "Fields in model: [stringField: java.lang.String]", 393 | "Getters in model: []" 394 | ); 395 | } 396 | } 397 | 398 | @Test 399 | public void shouldThrowExceptionWhenGetterMatchingToFieldHasDifferentType() { 400 | try { 401 | execution.run(BASE_PATH + "ModelWithNotMatchingGetterType.java"); 402 | fail("Should fail when getter type is different than type of field with matching name"); 403 | } catch (AggregatedException e) { 404 | assertThat(e.getExceptions(), hasSize(1)); 405 | assertStringEqual(e.getMessage(), "Verification failed:", 406 | "", 407 | "Class getter naming validation failed with following errors:", 408 | "", 409 | "1) Model {com.github.havardh.javaflow.model.ModelWithNotMatchingGetterType} is not a pure DTO.", 410 | "Type of getter getIntegerField (java.lang.String) does not correspond to field integerField (int)" 411 | ); 412 | } 413 | } 414 | } 415 | 416 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/JavaFlowTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow; 2 | 3 | import static com.github.havardh.javaflow.phases.parser.java.JavaParserFlags.defaultFlags; 4 | import static java.util.Arrays.stream; 5 | import static java.util.function.Function.identity; 6 | import static java.util.stream.Collectors.toMap; 7 | 8 | import static org.hamcrest.MatcherAssert.assertThat; 9 | import static org.hamcrest.Matchers.contains; 10 | import static org.hamcrest.Matchers.hasSize; 11 | import static org.hamcrest.Matchers.is; 12 | import static org.hamcrest.Matchers.notNullValue; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Optional; 17 | import java.util.stream.Collectors; 18 | 19 | import org.junit.jupiter.api.BeforeEach; 20 | import org.junit.jupiter.api.Nested; 21 | import org.junit.jupiter.api.Test; 22 | 23 | import com.github.havardh.javaflow.ast.Class; 24 | import com.github.havardh.javaflow.ast.Enum; 25 | import com.github.havardh.javaflow.ast.Field; 26 | import com.github.havardh.javaflow.ast.Parent; 27 | import com.github.havardh.javaflow.ast.Type; 28 | import com.github.havardh.javaflow.model.CanonicalName; 29 | import com.github.havardh.javaflow.phases.parser.Parser; 30 | import com.github.havardh.javaflow.phases.parser.java.JavaParser; 31 | import com.github.havardh.javaflow.phases.reader.FileReader; 32 | import com.github.havardh.javaflow.phases.transform.InheritanceTransformer; 33 | import com.github.havardh.javaflow.phases.transform.Transformer; 34 | import com.github.havardh.javaflow.phases.writer.flow.converter.JavaFlowConverter; 35 | 36 | public class JavaFlowTest { 37 | 38 | private static final String BASE_PATH = "src/test/java/com/github/havardh/javaflow/model/"; 39 | private static final JavaFlowConverter CONVERTER = new JavaFlowConverter(); 40 | 41 | @Nested 42 | class Classes { 43 | 44 | @Test 45 | public void shouldSetNameOfClass() { 46 | Type type = parse("Model"); 47 | 48 | assertThat(type.getCanonicalName().getName(), is("Model")); 49 | } 50 | 51 | @Test 52 | public void shouldSetPackageOfClass() { 53 | Type type = parse("Model"); 54 | 55 | assertThat(type.getCanonicalName().getPackageName(), is("com.github.havardh.javaflow.model")); 56 | } 57 | 58 | @Test 59 | public void shouldSetFieldOfClass() { 60 | Class aClass = (Class)parse("Model"); 61 | 62 | Field field = aClass.getFields().get(0); 63 | 64 | assertThat(field.getName(), is("yolo")); 65 | assertThat(field.getType().getCanonicalName().getName(), is("String")); 66 | } 67 | 68 | @Test 69 | public void shouldAddParentNameToDefinition() { 70 | Class aClass = (Class)parse("Sub"); 71 | 72 | assertThat(aClass.getParent() 73 | .map(Parent::getCanonicalName) 74 | .map(CanonicalName::getName).get(), 75 | is("Super") 76 | ); 77 | } 78 | 79 | @Nested 80 | class Types { 81 | 82 | @Nested 83 | class Primitives { 84 | private Map typeMap; 85 | 86 | @BeforeEach 87 | public void setup() { 88 | typeMap = typeMap((Class) parse("ModelWithPrimitive")); 89 | } 90 | 91 | @Test 92 | public void shouldMapByte() { 93 | assertThat(typeMap.get("byteField"), is("number")); 94 | } 95 | 96 | @Test 97 | public void shouldMapShort() { 98 | assertThat(typeMap.get("shortField"), is("number")); 99 | } 100 | 101 | @Test 102 | public void shouldMapInt() { 103 | assertThat(typeMap.get("intField"), is("number")); 104 | } 105 | 106 | @Test 107 | public void shouldMapLong() { 108 | assertThat(typeMap.get("longField"), is("number")); 109 | } 110 | 111 | @Test 112 | public void shouldMapFloat() { 113 | assertThat(typeMap.get("floatField"), is("number")); 114 | } 115 | 116 | @Test 117 | public void shouldMapDouble() { 118 | assertThat(typeMap.get("doubleField"), is("number")); 119 | } 120 | 121 | @Test 122 | public void shouldMapBoolean() { 123 | assertThat(typeMap.get("booleanField"), is("boolean")); 124 | } 125 | 126 | @Test 127 | public void shouldMapChar() { 128 | assertThat(typeMap.get("charField"), is("string")); 129 | } 130 | } 131 | 132 | @Nested 133 | class JavaLangObjects { 134 | private Map typeMap; 135 | 136 | @BeforeEach 137 | public void setup() { 138 | typeMap = typeMap((Class) parse("ModelWithJavaLangObjects")); 139 | } 140 | 141 | @Test 142 | public void shouldMapByte() { 143 | assertThat(typeMap.get("byteField"), is("number")); 144 | } 145 | 146 | @Test 147 | public void shouldMapShort() { 148 | assertThat(typeMap.get("shortField"), is("number")); 149 | } 150 | 151 | @Test 152 | public void shouldMapInt() { 153 | assertThat(typeMap.get("integerField"), is("number")); 154 | } 155 | 156 | @Test 157 | public void shouldMapLong() { 158 | assertThat(typeMap.get("longField"), is("number")); 159 | } 160 | 161 | @Test 162 | public void shouldMapFloat() { 163 | assertThat(typeMap.get("floatField"), is("number")); 164 | } 165 | 166 | @Test 167 | public void shouldMapDouble() { 168 | assertThat(typeMap.get("doubleField"), is("number")); 169 | } 170 | 171 | @Test 172 | public void shouldMapBoolean() { 173 | assertThat(typeMap.get("booleanField"), is("boolean")); 174 | } 175 | 176 | @Test 177 | public void shouldMapChar() { 178 | assertThat(typeMap.get("characterField"), is("string")); 179 | } 180 | 181 | @Test 182 | public void shouldMapString() { 183 | assertThat(typeMap.get("stringField"), is("string")); 184 | } 185 | 186 | } 187 | 188 | @Nested 189 | class Arrays { 190 | 191 | @Test 192 | public void shouldMapCharArrayToString() { 193 | Map types = typeMap((Class) parse("ModelWithCharArray")); 194 | 195 | assertThat(types.get("field"), is("string")); 196 | } 197 | } 198 | } 199 | 200 | @Nested 201 | public class Inheritance { 202 | Map typeMap; 203 | Class top, sup, sub; 204 | 205 | @BeforeEach 206 | public void setup() { 207 | typeMap = parseAll("Top", "Sub", "Super"); 208 | top = (Class) typeMap.get("Top"); 209 | sup = (Class) typeMap.get("Super"); 210 | sub = (Class) typeMap.get("Sub"); 211 | } 212 | 213 | @Test 214 | public void shouldParseAllModels() { 215 | assertThat(sup, is(notNullValue())); 216 | assertThat(sub, is(notNullValue())); 217 | } 218 | 219 | @Nested 220 | public class ParentLinks { 221 | 222 | @Test 223 | public void shouldSetSubParentLinkToSuper() { 224 | assertThat(sub.getParent().map(Parent::getReference).get(), is(sup)); 225 | } 226 | 227 | @Test 228 | public void shouldSetSupParentLinkToTop() { 229 | assertThat(sup.getParent().map(Parent::getReference).get(), is(top)); 230 | } 231 | 232 | @Test 233 | public void shouldNotSetTopParentLink() { 234 | assertThat(top.getParent(), is(Optional.empty())); 235 | } 236 | } 237 | 238 | @Nested 239 | public class Fields { 240 | 241 | @Nested 242 | public class Top { 243 | 244 | @Test 245 | public void shouldHaveASingleField() { 246 | assertThat(top.getFields(), hasSize(1)); 247 | assertThat(top.getFields().get(0).getName(), is("topField")); 248 | } 249 | } 250 | 251 | @Nested 252 | public class Super { 253 | 254 | @Test 255 | public void shouldInheritFieldFromTop() { 256 | assertThat(sup.getFields(), hasSize(2)); 257 | } 258 | 259 | @Test 260 | public void shouldHaveTopFieldsFirst() { 261 | assertThat(sup.getFields().get(0).getName(), is("topField")); 262 | } 263 | 264 | @Test 265 | public void shouldHaveOwnFieldLast() { 266 | assertThat(sup.getFields().get(1).getName(), is("superField")); 267 | } 268 | } 269 | 270 | @Nested 271 | public class Sub { 272 | 273 | @Test 274 | public void shouldInheritFieldFromAllParents() { 275 | assertThat(sub.getFields().size(), is(3)); 276 | } 277 | 278 | @Test 279 | public void shouldHaveFieldFromTopMostParentFirst() { 280 | assertThat(sub.getFields().get(0).getName(), is("topField")); 281 | } 282 | 283 | @Test 284 | public void shouldHaveAllParentFieldBeforeOwn() { 285 | assertThat(sub.getFields().get(1).getName(), is("superField")); 286 | } 287 | 288 | @Test 289 | public void shouldHaveOwnFieldLast() { 290 | assertThat(sub.getFields().get(2).getName(), is("subField")); 291 | } 292 | } 293 | } 294 | } 295 | } 296 | 297 | @Nested 298 | class Enums { 299 | 300 | @Test 301 | public void shouldSetNameOfEnum() { 302 | Type type = parse("Enumeration"); 303 | 304 | assertThat(type.getCanonicalName().getName(), is("Enumeration")); 305 | } 306 | 307 | @Test 308 | public void shouldSetPackageNameOfEnum() { 309 | Type type = parse("Enumeration"); 310 | 311 | assertThat(type.getCanonicalName().getPackageName(), is("com.github.havardh.javaflow.model")); 312 | } 313 | 314 | @Test 315 | public void shouldSetValuesOfEnum() { 316 | Enum anEnum = (Enum)parse("Enumeration"); 317 | 318 | assertThat(anEnum.getValues(), contains("ONE", "TWO")); 319 | } 320 | } 321 | 322 | @Nested 323 | class Packages { 324 | 325 | @Test 326 | public void shouldSetPackageNameForFields() { 327 | Class aClass = (Class)parse("Wrapper"); 328 | 329 | List fields = aClass.getFields(); 330 | 331 | Field member = fields.get(0); 332 | Field packagedMember = fields.get(1); 333 | 334 | assertThat(member.getType().getCanonicalName().getPackageName(), is("com.github.havardh.javaflow.model")); 335 | assertThat(packagedMember.getType().getCanonicalName().getPackageName(), 336 | is("com.github.havardh.javaflow.model.packaged")); 337 | } 338 | } 339 | 340 | private static Type parse(String name) { 341 | return new FileReader().read(BASE_PATH + name + ".java") 342 | .map(new JavaParser(defaultFlags())::parse) 343 | .filter(result -> !result.isEmpty()) 344 | .map(result -> result.get(0)) 345 | .orElse(null); 346 | } 347 | 348 | private static Map parseAll(String ...modelNames) { 349 | FileReader adapter = new FileReader(); 350 | Parser parser = new JavaParser(defaultFlags()); 351 | Transformer transformer = new InheritanceTransformer(); 352 | 353 | List types = stream(modelNames) 354 | .map(name -> BASE_PATH + name + ".java") 355 | .map(adapter::read) 356 | .map(Optional::get) 357 | .flatMap(source -> parser.parse(source).stream()) 358 | .collect(Collectors.toList()); 359 | 360 | transformer.transform(types); 361 | 362 | return types 363 | .stream() 364 | .collect(toMap(type -> type.getCanonicalName().getName(), identity())); 365 | } 366 | 367 | private static Map typeMap(Class aClass) { 368 | return aClass.getFields() 369 | .stream() 370 | .collect(toMap(Field::getName, JavaFlowTest::fieldToFlow)); 371 | } 372 | 373 | private static String fieldToFlow(Field field) { 374 | return CONVERTER.convert(field.getType().getCanonicalName()); 375 | } 376 | 377 | } 378 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/exceptions/AggregatedExceptionTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.exceptions; 2 | 3 | import static java.util.Arrays.asList; 4 | 5 | import static com.github.havardh.javaflow.testutil.Assertions.assertStringEqual; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class AggregatedExceptionTest { 10 | @Test 11 | public void shouldPrintMessageWithSubMessages() { 12 | AggregatedException exception = new AggregatedException( 13 | "Aggregated title", 14 | asList(new Exception("First exception"), new Exception("Second exception")), 15 | false 16 | ); 17 | 18 | assertStringEqual( 19 | exception.getMessage(), 20 | "Aggregated title", 21 | "First exception", 22 | "", 23 | "Second exception" 24 | ); 25 | } 26 | 27 | @Test 28 | public void shouldPrintMessageWithEnumeratedSubMessages() { 29 | AggregatedException exception = new AggregatedException( 30 | "Aggregated title", 31 | asList(new Exception("First exception"), new Exception("Second exception")), 32 | true 33 | ); 34 | 35 | assertStringEqual( 36 | exception.getMessage(), 37 | "Aggregated title", 38 | "1) First exception", 39 | "", 40 | "2) Second exception" 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/exceptions/FieldGettersMismatchExceptionTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.exceptions; 2 | 3 | import static com.github.havardh.javaflow.testutil.Assertions.assertStringEqual; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import com.github.havardh.javaflow.model.CanonicalName; 8 | 9 | public class FieldGettersMismatchExceptionTest { 10 | 11 | private static CanonicalName CANONICAL_NAME = CanonicalName.fromString("com.github.havardh.javaflow.Type"); 12 | 13 | @Test 14 | public void shouldPrintMessageWithMissingTypesDefinitions() { 15 | FieldGettersMismatchException exception = new FieldGettersMismatchException(CANONICAL_NAME, "an error messaage"); 16 | 17 | assertStringEqual( 18 | exception.getMessage(), 19 | "Model {com.github.havardh.javaflow.Type} is not a pure DTO.", 20 | "an error messaage" 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/exceptions/MissingTypeExceptionTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.exceptions; 2 | 3 | import static java.util.Collections.singletonList; 4 | 5 | import static com.github.havardh.javaflow.testutil.Assertions.assertStringEqual; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.junit.jupiter.api.Test; 12 | 13 | import com.github.havardh.javaflow.ast.Field; 14 | import com.github.havardh.javaflow.ast.Type; 15 | import com.github.havardh.javaflow.ast.builders.FieldBuilder; 16 | import com.github.havardh.javaflow.ast.builders.TypeBuilder; 17 | import com.github.havardh.javaflow.model.CanonicalName; 18 | 19 | public class MissingTypeExceptionTest { 20 | 21 | private static Type TYPE = TypeBuilder.object(CanonicalName.fromString("com.github.havardh.javaflow.Type")); 22 | private static Map> MISSING_TYPES_FOR_FIELDS = new HashMap<>(); 23 | 24 | static { 25 | MISSING_TYPES_FOR_FIELDS.put( 26 | TYPE, 27 | singletonList(FieldBuilder.fieldBuilder().withName("aField").withType(TYPE).build()) 28 | ); 29 | } 30 | 31 | @Test 32 | public void shouldPrintMessageWithMissingTypesDefinitions() { 33 | MissingTypeException exception = new MissingTypeException(MISSING_TYPES_FOR_FIELDS); 34 | 35 | assertStringEqual( 36 | exception.getMessage(), 37 | "Could not find types: {com.github.havardh.javaflow.Type=[aField: com.github.havardh.javaflow.Type]}" 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/Enumeration.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public enum Enumeration { 4 | 5 | ONE(1), TWO(2); 6 | 7 | 8 | private final int i; 9 | Enumeration(int i) { this.i = i;} 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/Member.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class Member { 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/Model.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class Model { 4 | 5 | private String yolo; 6 | 7 | public String getYolo() { 8 | return yolo; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithAnonymousClass.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithAnonymousClass { 4 | private Member field = new Member() { 5 | @Override 6 | public String toString() { 7 | return "Custom"; 8 | } 9 | }; 10 | 11 | public Member getField() { 12 | return field; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithCharArray.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithCharArray { 4 | char[] field; 5 | 6 | public char[] getField() { 7 | return field; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithCollection.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import java.util.Collection; 4 | 5 | public class ModelWithCollection { 6 | Collection strings; 7 | 8 | public Collection getStrings() { 9 | return strings; 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithFieldComments.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import javax.annotation.Generated; 4 | 5 | public class ModelWithFieldComments { 6 | // comment on field1 7 | private String field1; 8 | 9 | /* comment on field2 */ 10 | private String field2; 11 | 12 | /** comment on field3 */ 13 | private String field3; 14 | 15 | @Generated("to test dangling javadoc comments") 16 | /** comment on field4 */ 17 | private String field4; 18 | 19 | public String getField1() { 20 | return field1; 21 | } 22 | 23 | public String getField2() { 24 | return field2; 25 | } 26 | 27 | public String getField3() { 28 | return field3; 29 | } 30 | 31 | public String getField4() { 32 | return field4; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithGenericArrays.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithGenericArrays { 4 | private String[] field1; 5 | private int[] field2; 6 | 7 | public String[] getField1() { 8 | return field1; 9 | } 10 | 11 | public int[] getField2() { 12 | return field2; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithGenericMapKey.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public class ModelWithGenericMapKey { 7 | private Map, Integer> field; 8 | 9 | public Map, Integer> getField() { 10 | return field; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithInnerClasses.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import com.github.havardh.javaflow.model.ModelWithInnerClasses.Child1.Child2; 4 | import com.github.havardh.javaflow.model.ModelWithInnerClasses.Child1.Child2.Child3; 5 | import com.github.havardh.javaflow.model.ModelWithInnerClasses.Child1.Child2.Child4; 6 | 7 | public class ModelWithInnerClasses { 8 | class Child1 { 9 | class Child2 { 10 | class Child3 { 11 | private String field1; 12 | 13 | public String getField1() { 14 | return field1; 15 | } 16 | } 17 | 18 | class Child4 extends Child3 { 19 | } 20 | 21 | private int field1; 22 | 23 | public int getField1() { 24 | return field1; 25 | } 26 | } 27 | } 28 | 29 | private Child1 child1; 30 | private Child2 child2; 31 | private Child3 child3; 32 | private Child4 child4; 33 | 34 | public Child1 getChild1() { 35 | return child1; 36 | } 37 | 38 | public Child2 getChild2() { 39 | return child2; 40 | } 41 | 42 | public Child3 getChild3() { 43 | return child3; 44 | } 45 | 46 | public Child4 getChild4() { 47 | return child4; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithJavaLangObjects.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithJavaLangObjects { 4 | Boolean booleanField; 5 | Boolean booleanField2; 6 | Byte byteField; 7 | Character characterField; 8 | Double doubleField; 9 | Float floatField; 10 | Integer integerField; 11 | Long longField; 12 | Short shortField; 13 | String stringField; 14 | 15 | public Boolean getBooleanField() { 16 | return booleanField; 17 | } 18 | 19 | public Boolean isBooleanField2() { 20 | return booleanField2; 21 | } 22 | 23 | public Byte getByteField() { 24 | return byteField; 25 | } 26 | 27 | public Character getCharacterField() { 28 | return characterField; 29 | } 30 | 31 | public Double getDoubleField() { 32 | return doubleField; 33 | } 34 | 35 | public Float getFloatField() { 36 | return floatField; 37 | } 38 | 39 | public Integer getIntegerField() { 40 | return integerField; 41 | } 42 | 43 | public Long getLongField() { 44 | return longField; 45 | } 46 | 47 | public Short getShortField() { 48 | return shortField; 49 | } 50 | 51 | public String getStringField() { 52 | return stringField; 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithList.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import java.util.List; 4 | 5 | public class ModelWithList { 6 | private List words; 7 | 8 | public List getWords() { 9 | return words; 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithMap.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import java.util.Map; 4 | 5 | public class ModelWithMap { 6 | private Map field; 7 | 8 | public Map getField() { 9 | return field; 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithMapGenericValue.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public class ModelWithMapGenericValue { 7 | private Map> field; 8 | private Map>>> multipleNestedField; 9 | 10 | public Map> getField() { 11 | return field; 12 | } 13 | public Map>>> getMultipleNestedField() { 14 | return multipleNestedField; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithNotMatchingBooleanGetter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithNotMatchingBooleanGetter { 4 | private boolean booleanField; 5 | 6 | public boolean isBooleanFields() { 7 | return booleanField; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithNotMatchingGetter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithNotMatchingGetter { 4 | private String stringField; 5 | 6 | public String getStringFields() { 7 | return stringField; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithNotMatchingGetterType.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithNotMatchingGetterType { 4 | private int integerField; 5 | 6 | public String getIntegerField() { 7 | return "1"; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithNullableField.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | public class ModelWithNullableField { 6 | @Nullable 7 | private String field; 8 | 9 | @Nullable 10 | public String getField() { 11 | return field; 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithPrimitive.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithPrimitive { 4 | private byte byteField; 5 | private short shortField; 6 | private int intField; 7 | private long longField; 8 | private float floatField; 9 | private double doubleField; 10 | private boolean booleanField; 11 | private boolean booleanField2; 12 | private char charField; 13 | 14 | public byte getByteField() { 15 | return byteField; 16 | } 17 | 18 | public short getShortField() { 19 | return shortField; 20 | } 21 | 22 | public int getIntField() { 23 | return intField; 24 | } 25 | 26 | public long getLongField() { 27 | return longField; 28 | } 29 | 30 | public float getFloatField() { 31 | return floatField; 32 | } 33 | 34 | public double getDoubleField() { 35 | return doubleField; 36 | } 37 | 38 | public boolean isBooleanField() { 39 | return booleanField; 40 | } 41 | 42 | public boolean getBooleanField2() { 43 | return booleanField2; 44 | } 45 | 46 | public char getCharField() { 47 | return charField; 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithSet.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import java.util.Set; 4 | 5 | public class ModelWithSet { 6 | private Set strings; 7 | 8 | public Set getStrings() { 9 | return strings; 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithStaticFields.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithStaticFields { 4 | static String string; 5 | 6 | public static String getString() { 7 | return string; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithWildcardTypeParams.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | public class ModelWithWildcardTypeParams { 8 | private List wildcardList; 9 | private Map> wildcardMap; 10 | 11 | public List getWildcardList() { 12 | return wildcardList; 13 | } 14 | 15 | public Map> getWildcardMap() { 16 | return wildcardMap; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/ModelWithoutGetters.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class ModelWithoutGetters { 4 | private String stringField; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/Sub.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class Sub extends Super { 4 | private String subField; 5 | 6 | public String getSubField() { 7 | return subField; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/Super.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class Super extends Top { 4 | private String superField; 5 | 6 | public String getSuperField() { 7 | return superField; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/Top.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | public class Top { 4 | private String topField; 5 | 6 | public String getTopField() { 7 | return topField; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/Wrapper.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model; 2 | 3 | import com.github.havardh.javaflow.model.packaged.PackagedMember; 4 | 5 | public class Wrapper { 6 | Member member; 7 | PackagedMember packagedMember; 8 | 9 | public Member getMember() { 10 | return member; 11 | } 12 | 13 | public PackagedMember getPackagedMember() { 14 | return packagedMember; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/fixtures/CanonicalNameFixtures.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model.fixtures; 2 | 3 | import static com.github.havardh.javaflow.model.builders.CanonicalNameBuilder.canonicalName; 4 | 5 | import com.github.havardh.javaflow.model.builders.CanonicalNameBuilder; 6 | 7 | public class CanonicalNameFixtures { 8 | 9 | public static CanonicalNameBuilder stringName() { 10 | return canonicalName() 11 | .withName("String") 12 | .withPackageName("java.lang"); 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/fixtures/FieldDefinitionFixtures.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model.fixtures; 2 | 3 | import static com.github.havardh.javaflow.ast.builders.FieldBuilder.fieldBuilder; 4 | 5 | import com.github.havardh.javaflow.ast.builders.FieldBuilder; 6 | 7 | public class FieldDefinitionFixtures { 8 | 9 | public static FieldBuilder stringFieldDefinition() { 10 | return fieldBuilder() 11 | .withIsNullable(false) 12 | .withName("field") 13 | .withType(TypeFixtures.stringType() 14 | .build()); 15 | } 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/fixtures/TypeFixtures.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model.fixtures; 2 | 3 | import static com.github.havardh.javaflow.model.builders.CanonicalNameBuilder.canonicalName; 4 | import static com.github.havardh.javaflow.ast.builders.TypeBuilder.type; 5 | import static com.github.havardh.javaflow.model.fixtures.CanonicalNameFixtures.stringName; 6 | 7 | import com.github.havardh.javaflow.ast.Type; 8 | import com.github.havardh.javaflow.ast.builders.TypeBuilder; 9 | 10 | public class TypeFixtures { 11 | 12 | public static TypeBuilder stringType() { 13 | return type() 14 | .withName(stringName().build()); 15 | } 16 | 17 | public static TypeBuilder listType() { 18 | return type() 19 | .withName(canonicalName() 20 | .withName("List") 21 | .withPackageName("java.util") 22 | .build()) 23 | .withListType(new Type(stringName().build())); 24 | } 25 | 26 | 27 | 28 | public static TypeBuilder mapType() { 29 | return type() 30 | .withName(canonicalName() 31 | .withName("Map") 32 | .withPackageName("java.util") 33 | .build()) 34 | .withMapType( 35 | new Type(stringName().build()), 36 | new Type(stringName().build()) 37 | ); 38 | } 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/model/packaged/PackagedMember.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.model.packaged; 2 | 3 | public class PackagedMember { 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/filetransform/CommentPrependTransformerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.filetransform; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.equalTo; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | class CommentPrependTransformerTest { 9 | 10 | @Test 11 | public void shouldPrependFileWithComment() { 12 | CommentPrependTransformer transformer = new CommentPrependTransformer("comment"); 13 | 14 | String output = transformer.transform(""); 15 | 16 | assertThat(output, equalTo("/* comment */\n")); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/filetransform/EslintDisableTransformerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.filetransform; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.is; 5 | 6 | import java.util.Collections; 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | class EslintDisableTransformerTest { 11 | 12 | @Test 13 | public void shouldAppendEslintDisable() { 14 | FileTransformer transformer = new EslintDisableTransformer(Collections.singletonList( 15 | "no-unused-expressions" 16 | )); 17 | 18 | String output = transformer.transform(""); 19 | 20 | assertThat(output, is("/* eslint-disable no-unused-expressions */\n")); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/parser/java/CanonicalNameFactoryTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser.java; 2 | 3 | import static java.util.Collections.emptyMap; 4 | 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.Matchers.is; 7 | 8 | import java.util.Map; 9 | 10 | import org.hamcrest.Matchers; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import com.github.havardh.javaflow.model.CanonicalName; 14 | import com.github.havardh.javaflow.util.Maps; 15 | 16 | public class CanonicalNameFactoryTest { 17 | 18 | private static final String PACKAGE_NAME = "com.github.havardh"; 19 | 20 | @Test 21 | public void shouldFindAllJavaLangTypes() { 22 | Map imports = emptyMap(); 23 | 24 | CanonicalNameFactory factory = new CanonicalNameFactory(PACKAGE_NAME, imports); 25 | 26 | assertThat(factory.build("Boolean"), Matchers.is(CanonicalName.fromString("java.lang.Boolean"))); 27 | assertThat(factory.build("Byte"), Matchers.is(CanonicalName.fromString("java.lang.Byte"))); 28 | assertThat(factory.build("Character"), Matchers.is(CanonicalName.fromString("java.lang.Character"))); 29 | assertThat(factory.build("Double"), Matchers.is(CanonicalName.fromString("java.lang.Double"))); 30 | assertThat(factory.build("Float"), Matchers.is(CanonicalName.fromString("java.lang.Float"))); 31 | assertThat(factory.build("Integer"), Matchers.is(CanonicalName.fromString("java.lang.Integer"))); 32 | assertThat(factory.build("Long"), Matchers.is(CanonicalName.fromString("java.lang.Long"))); 33 | assertThat(factory.build("Short"), Matchers.is(CanonicalName.fromString("java.lang.Short"))); 34 | assertThat(factory.build("String"), Matchers.is(CanonicalName.fromString("java.lang.String"))); 35 | } 36 | 37 | @Test 38 | public void shouldFindJavaTypesInImports() { 39 | Map imports = Maps.collect(Maps.entry("MyType", "com.github.havardh.mypackage")); 40 | 41 | CanonicalNameFactory factory = new CanonicalNameFactory(PACKAGE_NAME, imports); 42 | 43 | assertThat(factory.build("MyType"), Matchers.is(CanonicalName.fromString("com.github.havardh.mypackage.MyType"))); 44 | } 45 | 46 | @Test 47 | public void shouldPlaceTypesNotFoundInImportOrLangPackageInGivenPackage() { 48 | Map imports = emptyMap(); 49 | 50 | CanonicalNameFactory factory = new CanonicalNameFactory(PACKAGE_NAME, imports); 51 | 52 | assertThat(factory.build("AType"), Matchers.is(CanonicalName.fromString(PACKAGE_NAME + ".AType"))); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/parser/java/TypeFactoryTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.parser.java; 2 | 3 | import static com.github.havardh.javaflow.util.Maps.collect; 4 | import static com.github.havardh.javaflow.util.Maps.entry; 5 | 6 | import static org.hamcrest.MatcherAssert.assertThat; 7 | import static org.hamcrest.Matchers.instanceOf; 8 | import static org.hamcrest.Matchers.is; 9 | 10 | import com.github.havardh.javaflow.ast.List; 11 | import com.github.havardh.javaflow.ast.Type; 12 | 13 | import org.junit.jupiter.api.Nested; 14 | import org.junit.jupiter.api.Test; 15 | 16 | public class TypeFactoryTest { 17 | 18 | @Nested 19 | public class Collections { 20 | 21 | private TypeFactory factory = new TypeFactory("com.github.havardh", collect(entry("Collection", "java.util"))); 22 | 23 | @Test 24 | public void shouldBeAnInstanceOfList() { 25 | Type type = factory.build("Collection", false); 26 | 27 | assertThat(type, instanceOf(List.class)); 28 | } 29 | 30 | @Test 31 | public void shouldBeAnInstanceOfListWhenSetType() { 32 | Type type = factory.build("Set", false); 33 | 34 | assertThat(type, instanceOf(List.class)); 35 | } 36 | 37 | @Test 38 | public void shouldSetSubType() { 39 | List list = (List)factory.build("Collection", false); 40 | 41 | assertThat(list.getType().getCanonicalName().getName(), is("String")); 42 | } 43 | 44 | @Test 45 | public void shouldSetSubTypeOfSubType() { 46 | List list = (List)factory.build("Collection>", false); 47 | List subtypeList = (List)list.getType(); 48 | 49 | assertThat(subtypeList.getType().getCanonicalName().getName(), is("String")); 50 | } 51 | } 52 | 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/resolver/FileResolverTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.resolver; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.util.List; 8 | 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | import static org.hamcrest.Matchers.*; 11 | import static org.hamcrest.core.IsEqual.equalTo; 12 | 13 | public class FileResolverTest { 14 | 15 | private static final String BASE_PATH = "src/test/java/com/github/havardh/javaflow/model/"; 16 | private static final String ABSOLUTE_BASE_PATH = Paths.get("./", BASE_PATH).normalize().toFile().getAbsolutePath() + "/"; 17 | private final FileResolver resolver = new FileResolver(); 18 | 19 | @Test 20 | public void shouldResolveFileByRelativePath() { 21 | List resolvedPaths = resolver.resolve(BASE_PATH + "Model.java"); 22 | 23 | assertThat(resolvedPaths, hasSize(1)); 24 | } 25 | 26 | @Test 27 | public void shouldResolveFileByAbsolutePath() { 28 | List resolvedPaths = resolver.resolve(ABSOLUTE_BASE_PATH + "Model.java"); 29 | 30 | assertThat(resolvedPaths, hasSize(1)); 31 | } 32 | 33 | @Test 34 | public void shouldResolveFilesInDirectoryByRelativePath() { 35 | List resolvedPaths = resolver.resolve(BASE_PATH); 36 | 37 | assertThat(resolvedPaths, hasSize(greaterThan(0))); 38 | } 39 | 40 | @Test 41 | public void shouldResolveFilesInDirectoryByAbsolutePath() { 42 | List resolvedPaths = resolver.resolve(ABSOLUTE_BASE_PATH); 43 | 44 | assertThat(resolvedPaths, hasSize(greaterThan(0))); 45 | } 46 | 47 | @Test 48 | public void shouldResolveFilesInChildDirectories() { 49 | List resolvedPaths = resolver.resolve(BASE_PATH); 50 | 51 | Path expectedPath = Paths.get(BASE_PATH, "packaged/PackagedMember.java"); 52 | assertThat(resolvedPaths, hasItem(equalTo(expectedPath))); 53 | } 54 | 55 | @Test 56 | public void shouldResolveFileByRelativeGlobPattern() { 57 | List paths = resolver.resolve(BASE_PATH + "**/*.java"); 58 | 59 | assertThat(paths, hasSize(greaterThan(0))); 60 | } 61 | 62 | @Test 63 | public void shouldResolveFileByAbsoluteGlobPattern() { 64 | List paths = resolver.resolve(ABSOLUTE_BASE_PATH + "**/*.java"); 65 | 66 | assertThat(paths, hasSize(greaterThan(0))); 67 | } 68 | 69 | @Test 70 | public void shouldResolveFileByPathSegment() { 71 | List paths = resolver.resolve("**/packaged/*.java"); 72 | 73 | Path expectedPath = Paths.get(BASE_PATH, "packaged/PackagedMember.java"); 74 | assertThat(paths, contains(equalTo(expectedPath))); 75 | } 76 | 77 | @Test 78 | public void shouldResolveRelativeGlobPatternLocally() { 79 | List paths = resolver.resolve("*.java"); 80 | 81 | assertThat(paths, hasSize(0)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/transform/InheritanceTransformerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.transform; 2 | 3 | import static java.util.Arrays.asList; 4 | 5 | import static com.github.havardh.javaflow.ast.builders.EnumBuilder.enumBuilder; 6 | 7 | import static org.hamcrest.MatcherAssert.assertThat; 8 | import static org.hamcrest.Matchers.hasSize; 9 | import static org.hamcrest.Matchers.is; 10 | 11 | import java.util.List; 12 | 13 | import com.github.havardh.javaflow.ast.Parent; 14 | import com.github.havardh.javaflow.ast.Type; 15 | import com.github.havardh.javaflow.ast.Class; 16 | import com.github.havardh.javaflow.model.CanonicalName; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import com.github.havardh.javaflow.ast.builders.ClassBuilder; 21 | 22 | public class InheritanceTransformerTest { 23 | 24 | private Transformer transformer = new InheritanceTransformer(); 25 | 26 | @Test 27 | public void shouldKeepAllTypes() { 28 | List types = asList( 29 | ClassBuilder.classBuilder().build(), 30 | enumBuilder().build() 31 | ); 32 | 33 | transformer.transform(types); 34 | 35 | assertThat(types, hasSize(2)); 36 | } 37 | 38 | @Test 39 | public void shouldLinkClassesWithParentOnMatchingName() { 40 | Class parent = ClassBuilder.classBuilder() 41 | .withName("Parent") 42 | .withPackageName("com.github.havardh.a") 43 | .build(); 44 | 45 | Class child = ClassBuilder.classBuilder() 46 | .withName("Child") 47 | .withParent(new Parent(CanonicalName.object("com.github.havardh.a", "Parent"))) 48 | .build(); 49 | 50 | transformer.transform(asList(parent, child)); 51 | 52 | assertThat(child.getParent().get().getReference(), is(parent)); 53 | } 54 | 55 | @Test 56 | public void shouldResolveNameConflictsWithPackageName() { 57 | Class parentA = ClassBuilder.classBuilder() 58 | .withName("Parent") 59 | .withPackageName("com.github.havardh.a") 60 | .build(); 61 | 62 | Class parentB = ClassBuilder.classBuilder() 63 | .withName("Parent") 64 | .withPackageName("com.github.havardh.b") 65 | .build(); 66 | 67 | Class child = ClassBuilder.classBuilder() 68 | .withName("Child") 69 | .withParent(new Parent(CanonicalName.object("com.github.havardh.a", "Parent"))) 70 | .build(); 71 | 72 | transformer.transform(asList(parentA, parentB, child)); 73 | 74 | assertThat(child.getParent().get().getReference(), is(parentA)); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/transform/SortedTypeTransformerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.transform; 2 | 3 | import static java.util.Arrays.asList; 4 | 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.Matchers.contains; 7 | 8 | import java.util.List; 9 | 10 | import com.github.havardh.javaflow.ast.Type; 11 | 12 | import org.junit.jupiter.api.Test; 13 | 14 | import com.github.havardh.javaflow.ast.builders.ClassBuilder; 15 | 16 | public class SortedTypeTransformerTest { 17 | 18 | private static final Type A = ClassBuilder.classBuilder().withName("A").build(); 19 | private static final Type B = ClassBuilder.classBuilder().withName("b").build(); 20 | private static final Type C = ClassBuilder.classBuilder().withName("C").build(); 21 | 22 | private Transformer transformer = new SortedTypeTransformer(); 23 | 24 | @Test 25 | public void shouldSortTypesBasedOnName() { 26 | List types = asList(C, B, A); 27 | 28 | transformer.transform(types); 29 | 30 | assertThat(types, contains(A, B, C)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/verifier/ClassGetterNamingVerifierTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.verifier; 2 | 3 | import static java.util.Arrays.asList; 4 | import static java.util.Collections.singletonList; 5 | 6 | import static org.hamcrest.MatcherAssert.assertThat; 7 | import static org.hamcrest.Matchers.hasSize; 8 | import static org.junit.jupiter.api.Assertions.assertThrows; 9 | 10 | import org.junit.jupiter.api.Test; 11 | 12 | import com.github.havardh.javaflow.ast.Class; 13 | import com.github.havardh.javaflow.ast.Type; 14 | import com.github.havardh.javaflow.ast.builders.ClassBuilder; 15 | import com.github.havardh.javaflow.ast.builders.FieldBuilder; 16 | import com.github.havardh.javaflow.ast.builders.MethodBuilder; 17 | import com.github.havardh.javaflow.ast.builders.TypeBuilder; 18 | import com.github.havardh.javaflow.exceptions.AggregatedException; 19 | import com.github.havardh.javaflow.model.CanonicalName; 20 | 21 | class ClassGetterNamingVerifierTest { 22 | 23 | private static final Type PRIMITIVE_INT = TypeBuilder.primitive(CanonicalName.primitive("int")); 24 | private static final Type PRIMITIVE_DOUBLE = TypeBuilder.primitive(CanonicalName.primitive("double")); 25 | 26 | private ClassGetterNamingVerifier verifier = new ClassGetterNamingVerifier(); 27 | 28 | @Test 29 | public void shouldPassForModelWithMatchingGetters() { 30 | Class aClass = ClassBuilder.classBuilder() 31 | .withPackageName("com.github.havardh.models") 32 | .withName("Class") 33 | .withField(FieldBuilder.fieldBuilder() 34 | .withName("someField") 35 | .withType(PRIMITIVE_INT) 36 | .build()) 37 | .withGetter(MethodBuilder.methodBuilder() 38 | .withName("getSomeField") 39 | .withType(PRIMITIVE_INT) 40 | .build()) 41 | .build(); 42 | 43 | verifier.verify(singletonList(aClass)); 44 | } 45 | 46 | @Test 47 | public void shouldFailForModelWithGetterWithNonMatchingType() { 48 | Class aClass = ClassBuilder.classBuilder() 49 | .withPackageName("com.github.havardh.models") 50 | .withName("Class") 51 | .withField(FieldBuilder.fieldBuilder() 52 | .withName("someField") 53 | .withType(PRIMITIVE_INT) 54 | .build()) 55 | .withGetter(MethodBuilder.methodBuilder() 56 | .withName("getSomeField") 57 | .withType(PRIMITIVE_DOUBLE) 58 | .build()) 59 | .build(); 60 | 61 | AggregatedException exception = assertThrows( 62 | AggregatedException.class, 63 | () -> verifier.verify(singletonList(aClass)) 64 | ); 65 | 66 | assertThat(exception.getExceptions(), hasSize(1)); 67 | } 68 | 69 | @Test 70 | public void shouldFailForModelWithGetterWithNonMatchingName() { 71 | Class aClass = ClassBuilder.classBuilder() 72 | .withPackageName("com.github.havardh.models") 73 | .withName("Class") 74 | .withField(FieldBuilder.fieldBuilder() 75 | .withName("someField") 76 | .withType(PRIMITIVE_INT) 77 | .build()) 78 | .withGetter(MethodBuilder.methodBuilder() 79 | .withName("getSomeFields") 80 | .withType(PRIMITIVE_INT) 81 | .build()) 82 | .build(); 83 | 84 | AggregatedException exception = assertThrows( 85 | AggregatedException.class, 86 | () -> verifier.verify(singletonList(aClass)) 87 | ); 88 | 89 | assertThat(exception.getExceptions(), hasSize(1)); 90 | } 91 | 92 | @Test 93 | public void shouldFailForModelWithoutGetters() { 94 | Class aClass = ClassBuilder.classBuilder() 95 | .withPackageName("com.github.havardh.models") 96 | .withName("Class") 97 | .withField(FieldBuilder.fieldBuilder() 98 | .withName("someField") 99 | .withType(PRIMITIVE_INT) 100 | .build()) 101 | .build(); 102 | 103 | AggregatedException exception = assertThrows( 104 | AggregatedException.class, 105 | () -> verifier.verify(singletonList(aClass)) 106 | ); 107 | 108 | assertThat(exception.getExceptions(), hasSize(1)); 109 | } 110 | 111 | @Test 112 | public void shouldFailAndReportMultipleFieldsPerClass() { 113 | Class aClass = ClassBuilder.classBuilder() 114 | .withPackageName("com.github.havardh.models") 115 | .withName("Class") 116 | .withField(FieldBuilder.fieldBuilder() 117 | .withName("fieldOne") 118 | .withType(PRIMITIVE_INT) 119 | .build()) 120 | .withField(FieldBuilder.fieldBuilder() 121 | .withName("fieldTwo") 122 | .withType(PRIMITIVE_INT) 123 | .build()) 124 | .withGetter(MethodBuilder.methodBuilder().withName("getFieldOnes").withType(PRIMITIVE_INT).build()) 125 | .withGetter(MethodBuilder.methodBuilder().withName("getFieldTwos").withType(PRIMITIVE_INT).build()) 126 | .build(); 127 | 128 | AggregatedException exception = assertThrows( 129 | AggregatedException.class, 130 | () -> verifier.verify(singletonList(aClass)) 131 | ); 132 | 133 | assertThat(exception.getExceptions(), hasSize(2)); 134 | } 135 | 136 | @Test 137 | public void shouldFailAndReportOnMultipleErroneousClasses() { 138 | Class aClass = ClassBuilder.classBuilder() 139 | .withPackageName("com.github.havardh.models") 140 | .withName("aClass") 141 | .withField(FieldBuilder.fieldBuilder() 142 | .withName("fieldOne") 143 | .withType(PRIMITIVE_INT) 144 | .build()) 145 | .build(); 146 | Class bClass = ClassBuilder.classBuilder() 147 | .withPackageName("com.github.havardh.models") 148 | .withName("bClass") 149 | .withField(FieldBuilder.fieldBuilder() 150 | .withName("fieldOne") 151 | .withType(PRIMITIVE_INT) 152 | .build()) 153 | .build(); 154 | 155 | AggregatedException exception = assertThrows( 156 | AggregatedException.class, 157 | () -> verifier.verify(asList(aClass, bClass)) 158 | ); 159 | 160 | assertThat(exception.getExceptions(), hasSize(2)); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/verifier/MemberFieldsPresentVerifierTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.verifier; 2 | 3 | import static java.util.Collections.singletonList; 4 | 5 | import static com.github.havardh.javaflow.ast.builders.FieldBuilder.fieldBuilder; 6 | import static com.github.havardh.javaflow.model.TypeMap.emptyTypeMap; 7 | 8 | import static org.hamcrest.MatcherAssert.assertThat; 9 | import static org.hamcrest.Matchers.is; 10 | import static org.junit.jupiter.api.Assertions.assertThrows; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import com.github.havardh.javaflow.ast.Class; 16 | import com.github.havardh.javaflow.ast.Field; 17 | import com.github.havardh.javaflow.ast.Type; 18 | import com.github.havardh.javaflow.ast.builders.ClassBuilder; 19 | import com.github.havardh.javaflow.exceptions.MissingTypeException; 20 | import com.github.havardh.javaflow.model.CanonicalName; 21 | 22 | import org.junit.jupiter.api.Test; 23 | 24 | import com.github.havardh.javaflow.ast.builders.TypeBuilder; 25 | 26 | public class MemberFieldsPresentVerifierTest { 27 | 28 | private Verifier verifier = new MemberFieldsPresentVerifier(emptyTypeMap()); 29 | 30 | @Test 31 | public void shouldPassTypeWithoutMembers() { 32 | verifier.verify(singletonList(TypeBuilder.type() 33 | .withName(CanonicalName.fromString("com.github.havardh.Test1")) 34 | .build())); 35 | } 36 | 37 | @Test 38 | public void shouldPassForBuiltInTypes() { 39 | Class aClass = ClassBuilder.classBuilder() 40 | .withName("Test1") 41 | .withField(fieldBuilder() 42 | .withName("name") 43 | .withType(TypeBuilder.type() 44 | .withName(CanonicalName.fromString("java.lang.String")) 45 | .build()) 46 | .build()) 47 | .build(); 48 | 49 | verifier.verify(singletonList(aClass)); 50 | } 51 | 52 | @Test 53 | public void shouldFailTypeWithMissingMember() { 54 | Field field = fieldBuilder() 55 | .withName("name") 56 | .withType(TypeBuilder.type() 57 | .withName(CanonicalName.fromString("com.github.havardh.Type")) 58 | .build()) 59 | .build(); 60 | 61 | Class aClass = ClassBuilder.classBuilder() 62 | .withName("Test1") 63 | .withField(field) 64 | .build(); 65 | 66 | MissingTypeException exception = assertThrows(MissingTypeException.class, () -> 67 | verifier.verify(singletonList(aClass)) 68 | ); 69 | 70 | Map> missing = exception.getTypes(); 71 | 72 | assertThat(missing.get(aClass), is(singletonList(field))); 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/writer/WriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer; 2 | 3 | import static java.lang.String.format; 4 | 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.Matchers.hasSize; 7 | import static org.hamcrest.Matchers.is; 8 | 9 | import java.io.IOException; 10 | import java.io.StringWriter; 11 | 12 | public class WriterTest { 13 | 14 | private final Writer writer; 15 | 16 | public WriterTest(Writer writer) { 17 | this.writer = writer; 18 | } 19 | 20 | protected String toFlow(T t) throws IOException { 21 | StringWriter stringWriter = new StringWriter(); 22 | writer.write(t, stringWriter); 23 | return stringWriter.toString(); 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/writer/flow/ClassWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import static com.github.havardh.javaflow.model.fixtures.FieldDefinitionFixtures.stringFieldDefinition; 4 | import static com.github.havardh.javaflow.testutil.Assertions.assertStringEqual; 5 | import static com.github.havardh.javaflow.testutil.MapConverterFixture.stringMap; 6 | 7 | import java.io.IOException; 8 | 9 | import com.github.havardh.javaflow.ast.Class; 10 | import com.github.havardh.javaflow.phases.writer.WriterTest; 11 | 12 | import org.junit.jupiter.api.Test; 13 | 14 | import com.github.havardh.javaflow.ast.builders.ClassBuilder; 15 | 16 | public class ClassWriterTest extends WriterTest { 17 | 18 | public ClassWriterTest() { 19 | super(new ClassWriter(stringMap().build())); 20 | } 21 | 22 | @Test 23 | public void shouldWriteClassDefinitionForEmptyModel() throws IOException { 24 | String flow = toFlow(ClassBuilder.classBuilder().withName("Model").build()); 25 | 26 | assertStringEqual(flow, "export type Model = {};"); 27 | } 28 | 29 | @Test 30 | public void shouldFieldDefinitions() throws IOException { 31 | String flow = toFlow(ClassBuilder.classBuilder() 32 | .withName("Model") 33 | .withField(stringFieldDefinition().build()) 34 | .build()); 35 | 36 | assertStringEqual(flow, 37 | "export type Model = {", 38 | " field: string,", 39 | "};" 40 | ); 41 | } 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/writer/flow/EnumWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import static com.github.havardh.javaflow.testutil.Assertions.assertStringEqual; 4 | 5 | import java.io.IOException; 6 | 7 | import com.github.havardh.javaflow.ast.Enum; 8 | import com.github.havardh.javaflow.ast.builders.EnumBuilder; 9 | import com.github.havardh.javaflow.phases.writer.WriterTest; 10 | 11 | import org.junit.jupiter.api.Test; 12 | 13 | public class EnumWriterTest extends WriterTest { 14 | 15 | public EnumWriterTest() { 16 | super(new EnumWriter()); 17 | } 18 | 19 | @Test 20 | public void shouldSerializeEnum() throws IOException { 21 | String flow = toFlow(EnumBuilder.enumBuilder() 22 | .withName("Enum") 23 | .withEnumValue("Value1") 24 | .withEnumValue("Value2") 25 | .build()); 26 | 27 | assertStringEqual(flow, 28 | "export type Enum =", 29 | " | \"Value1\"", 30 | " | \"Value2\";" 31 | ); 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/writer/flow/FieldWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import static com.github.havardh.javaflow.ast.builders.FieldBuilder.fieldBuilder; 4 | import static com.github.havardh.javaflow.testutil.Assertions.assertStringEqual; 5 | import static com.github.havardh.javaflow.testutil.MapConverterFixture.stringMap; 6 | 7 | import java.io.IOException; 8 | 9 | import com.github.havardh.javaflow.ast.Field; 10 | import com.github.havardh.javaflow.phases.writer.WriterTest; 11 | 12 | import org.junit.jupiter.api.Test; 13 | 14 | import com.github.havardh.javaflow.model.fixtures.TypeFixtures; 15 | 16 | public class FieldWriterTest extends WriterTest { 17 | 18 | public FieldWriterTest() { 19 | super(new FieldDefinitionWriter(stringMap().build())); 20 | } 21 | 22 | @Test 23 | public void shouldSerializeFieldDefinition() throws IOException { 24 | String flow = toFlow(fieldBuilder() 25 | .withName("field") 26 | .withType(TypeFixtures.stringType().build()) 27 | .build()); 28 | 29 | assertStringEqual(flow, 30 | "field: string" 31 | ); 32 | } 33 | 34 | @Test 35 | public void shouldPrependTypeOfNullableWithQuestionMark() throws IOException { 36 | String flow = toFlow(fieldBuilder() 37 | .withName("field") 38 | .withIsNullable(true) 39 | .withType(TypeFixtures.stringType().build()) 40 | .build()); 41 | 42 | assertStringEqual(flow, 43 | "field: ?string" 44 | ); 45 | } 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/writer/flow/TypeWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.is; 5 | 6 | import java.io.IOException; 7 | 8 | import com.github.havardh.javaflow.ast.Type; 9 | import com.github.havardh.javaflow.phases.writer.WriterTest; 10 | 11 | import org.junit.jupiter.api.Test; 12 | 13 | import com.github.havardh.javaflow.model.fixtures.TypeFixtures; 14 | import com.github.havardh.javaflow.testutil.MapConverterFixture; 15 | 16 | public class TypeWriterTest extends WriterTest { 17 | 18 | public TypeWriterTest() { 19 | super(new TypeWriter(MapConverterFixture.stringMap() 20 | .with("java.util.List", "Array") 21 | .build())); 22 | } 23 | 24 | @Test 25 | public void shouldSerializeType() throws IOException { 26 | String flow = toFlow(TypeFixtures.stringType().build()); 27 | 28 | assertThat(flow, is("string")); 29 | } 30 | 31 | @Test 32 | public void shouldSerializeListType() throws IOException { 33 | String flow = toFlow(TypeFixtures.listType().build()); 34 | 35 | assertThat(flow, is("Array")); 36 | } 37 | 38 | @Test 39 | public void shouldSerializeMapType() throws IOException { 40 | String flow = toFlow(TypeFixtures.mapType().build()); 41 | 42 | assertThat(flow, is("{[key: string]: string}")); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/phases/writer/flow/converter/JavaFlowConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.phases.writer.flow.converter; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.is; 5 | 6 | import com.github.havardh.javaflow.model.CanonicalName; 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | public class JavaFlowConverterTest { 11 | 12 | private JavaFlowConverter converter = new JavaFlowConverter(); 13 | 14 | @Test 15 | public void shouldConvertCollectionToArray() { 16 | String flowType = converter.convert(CanonicalName.fromString("java.util.Collection")); 17 | 18 | assertThat(flowType, is("Array")); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/testutil/Assertions.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.testutil; 2 | 3 | import static java.util.Arrays.stream; 4 | import static java.util.stream.Collectors.joining; 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | 7 | public final class Assertions { 8 | 9 | private Assertions() { 10 | } 11 | 12 | public static void assertStringEqual(String actual, String... expected) { 13 | assertEquals(join(expected), actual); 14 | } 15 | 16 | private static String join(String ...expected) { 17 | return stream(expected).collect(joining("\n")); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/testutil/MapConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.testutil; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.github.havardh.javaflow.model.CanonicalName; 7 | import com.github.havardh.javaflow.phases.writer.flow.converter.Converter; 8 | 9 | public class MapConverter implements Converter { 10 | 11 | private Map map; 12 | 13 | private MapConverter(Map map) { 14 | this.map = map; 15 | } 16 | 17 | public static MapConverterBuilder builder() { 18 | return new MapConverterBuilder(); 19 | } 20 | 21 | public static class MapConverterBuilder { 22 | private Map map = new HashMap<>(); 23 | 24 | public MapConverterBuilder with(String cName, String type) { 25 | return with(CanonicalName.fromString(cName), type); 26 | } 27 | 28 | public MapConverterBuilder with(CanonicalName name, String type) { 29 | map.put(name, type); 30 | return this; 31 | } 32 | 33 | public MapConverter build() { 34 | return new MapConverter(map); 35 | } 36 | 37 | } 38 | 39 | @Override 40 | public String convert(CanonicalName name) { 41 | return map.get(name); 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/test/java/com/github/havardh/javaflow/testutil/MapConverterFixture.java: -------------------------------------------------------------------------------- 1 | package com.github.havardh.javaflow.testutil; 2 | 3 | import static com.github.havardh.javaflow.testutil.MapConverter.builder; 4 | 5 | import com.github.havardh.javaflow.model.fixtures.CanonicalNameFixtures; 6 | 7 | public class MapConverterFixture { 8 | 9 | public static MapConverter.MapConverterBuilder stringMap() { 10 | return builder().with(CanonicalNameFixtures.stringName().build(), "string"); 11 | } 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/test/resources/log4j2-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | --------------------------------------------------------------------------------