pax_global_header00006660000000000000000000000064133650060360014514gustar00rootroot0000000000000052 comment=38622249118deccb25651a3f1264db2e24881a1d lightvalue-0.8.1/000077500000000000000000000000001336500603600136665ustar00rootroot00000000000000lightvalue-0.8.1/.gitignore000077500000000000000000000002161336500603600156600ustar00rootroot00000000000000# IntelliJ IDEA .idea *.iml # gradle wrapper gradlew* gradle/wrapper */gradle/wrapper # Other .gradle */out /build */build examples/*/build lightvalue-0.8.1/CHANGELOG.md000066400000000000000000000007441336500603600155040ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.8.1] ### Changed - Fix: generate valid equals for empty model class - Fix: make JacksonMixIns accessible from different package ## 0.8.0 ### Added - TODO [0.8.1]: https://gitlab.com/kravemir/lightvalue/compare/0.8.0...0.8.1 lightvalue-0.8.1/LICENSE000066400000000000000000000261561336500603600147050ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018 Miroslav Kravec Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. lightvalue-0.8.1/README.md000066400000000000000000000060721336500603600151520ustar00rootroot00000000000000LightValue ========== LightValue is made to generate models for libraries as value classes, which are also JSON serialization friendly. The primary goal is: * **NO mandatory runtime DEPENDENCIES** for generated value classes. Usage of Gradle Plugin ----- ### Include in gradle project ```groovy buildscript { repositories { mavenCentral() } dependencies { classpath 'org.kravemir.lightvalue:lightvalue-gradle-plugin:0.8.1' } } apply plugin: 'java' apply plugin: 'org.kravemir.lightvalue' ``` ### Default convention Default convention creates a lightvalue configuration for each java sourceset, if Java plugin as applied. Read more about default convention approach in [Convention over configuration](https://guides.gradle.org/designing-gradle-plugins/#convention_over_configuration) guide. See [examples](#examples), as they utilize default convention. ### Custom convention To use own custom conventions, apply only `LightValueBasePlugin`, or create custom tasks of type `LightValueGeneratorTask`. ### Configuration Each configuration is of `LightValueConfiguration` java type, and has following properties and methods: | Name | Description | | -------------------------- | ------------------------------------------------------------ | | **`name`** *(read-only)* | Name of configuration | | **`source`** | method adds `path` (single parameter) to list of sources | | **`outputDir`** | property defines output directory for generation of value classes | | **`registerInSourceSets`** | method registers generated output to `sourceSets`(varargs parameter), and adds dependency to each source set compile java task | ### Examples | Name | Description | | ------------------------------------------------------- | ------------------------------------------------------------ | | [01. Hello Value Models](examples/01-hello-value-model) | Just, a Hello World for LightValue models using gradle | | [02. Data Types](examples/02-data-types) | [TODO] will contain supported data types | | [03. JSON](examples/03-json) | Usage optional integration with Jackson | | [04. multi project](examples/04-multiproject) | [TODO] will contain example how to use in multi (sub)projects and depedencies | Usage of Java library --------------------- TODO: not yet documented Quality Assurance ----------------- The quality assurance is based on [Testing Gradle plugins](https://guides.gradle.org/testing-gradle-plugins/) guide: * manual tests based on [examples](examples), and check that: * end-user API remains unchanged across versions, * generated stuff works as expected, * unit tests in [generator's tests](lightvalue-generator/src/test), * and, nothing else now,... License ------- The whole project licensed is under Apache-2.0 license. lightvalue-0.8.1/bin/000077500000000000000000000000001336500603600144365ustar00rootroot00000000000000lightvalue-0.8.1/bin/publishToMavenLocal.sh000077500000000000000000000002221336500603600207040ustar00rootroot00000000000000#!/usr/bin/env bash ./gradlew wrapper --gradle-version 4.10.2 ./gradlew --console=plain \ -PenablePublishing \ clean publishToMavenLocallightvalue-0.8.1/bin/publishToSonatypeSnapshots.sh000077500000000000000000000005401336500603600223730ustar00rootroot00000000000000#!/usr/bin/env bash ./gradlew wrapper --gradle-version 4.10.2 ./gradlew --console=plain \ -PenablePublishing \ -PsonatypeUsername=kravemir \ clean \ publishLibraryMavenPublicationToSonatypeSnapshots \ publishPluginMavenPublicationToSonatypeSnapshots \ publishLightvaluePluginMarkerMavenPublicationToSonatypeSnapshotsRepositorylightvalue-0.8.1/build.gradle000066400000000000000000000066101336500603600161500ustar00rootroot00000000000000import groovy.swing.SwingBuilder gradle.taskGraph.whenReady { taskGraph -> if (!taskGraph.allTasks.findAll { it.name ==~ /.*Sonatype.*/ }.isEmpty()) { String password = null if (System.console() == null) { new SwingBuilder().edt { dialog(modal: true, title: 'Enter password', alwaysOnTop: true, resizable: false, locationRelativeTo: null, pack: true, show: true ) { vbox { label(text: "Please enter key passphrase:") input = passwordField() button(defaultButton: true, text: 'OK', actionPerformed: { password = new String(input.password) dispose(); }) } } } } else { def passwordRaw = System.console().readPassword("\nPlease enter password: ") password = new String(passwordRaw) } rootProject.subprojects { project -> signing { useGpgCmd() } project.publishing.repositories.each { repo -> repo.credentials.password = password } } } } subprojects { subproject -> apply plugin: 'java' apply plugin: 'maven-publish' apply plugin: 'signing' apply plugin: 'idea' group 'org.kravemir.lightvalue' version '0.8.1' sourceCompatibility = 1.8 apply from: "${rootProject.projectDir}/gradle/publishing.gradle" ext { publishingEnabled = project.hasProperty("enablePublishing") sonatypeUsername = project.hasProperty('sonatypeUsername') ? project.getProperty('sonatypeUsername') : '' } repositories { mavenCentral() } task sourceJar(type: Jar, dependsOn: classes) { classifier 'sources' from sourceSets.main.allSource } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } tasks.withType(Jar) { from(rootProject.projectDir) { include 'LICENSE' into 'META-INF' } manifest { attributes( 'Created-By': System.getProperty('java.version') + ' (' + System.getProperty('java.vendor') + ')', 'Implementation-Title': subproject.artifactName, 'Implementation-Version': subproject.version ) } } artifacts { archives jar archives sourceJar archives javadocJar } publishing { repositories { maven { name "sonatypeSnapshots" url 'https://oss.sonatype.org/content/repositories/snapshots/' credentials { username project.sonatypeUsername password "-- not specified --" } } } } afterEvaluate { if (publishingEnabled) { signing { publishing.publications.all { publication -> sign publication required { !gradle.taskGraph.allTasks.findAll { it.name ==~ /.*Sonatype.*/ }.isEmpty() } } } } } } lightvalue-0.8.1/buildSrc/000077500000000000000000000000001336500603600154355ustar00rootroot00000000000000lightvalue-0.8.1/buildSrc/build.gradle000066400000000000000000000002121336500603600177070ustar00rootroot00000000000000apply plugin: 'java' repositories { mavenCentral() } dependencies { compile group: 'de.jflex', name: 'jflex', version: '1.6.1' }lightvalue-0.8.1/buildSrc/src/000077500000000000000000000000001336500603600162245ustar00rootroot00000000000000lightvalue-0.8.1/buildSrc/src/main/000077500000000000000000000000001336500603600171505ustar00rootroot00000000000000lightvalue-0.8.1/buildSrc/src/main/java/000077500000000000000000000000001336500603600200715ustar00rootroot00000000000000lightvalue-0.8.1/buildSrc/src/main/java/org/000077500000000000000000000000001336500603600206605ustar00rootroot00000000000000lightvalue-0.8.1/buildSrc/src/main/java/org/kravemir/000077500000000000000000000000001336500603600225005ustar00rootroot00000000000000lightvalue-0.8.1/buildSrc/src/main/java/org/kravemir/lightvalue/000077500000000000000000000000001336500603600246445ustar00rootroot00000000000000lightvalue-0.8.1/buildSrc/src/main/java/org/kravemir/lightvalue/buildsrc/000077500000000000000000000000001336500603600264535ustar00rootroot00000000000000lightvalue-0.8.1/buildSrc/src/main/java/org/kravemir/lightvalue/buildsrc/GenerateLexerTask.java000066400000000000000000000020511336500603600326710ustar00rootroot00000000000000package org.kravemir.lightvalue.buildsrc; import jflex.Main; import jflex.SilentExit; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.SourceTask; import org.gradle.api.tasks.TaskAction; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class GenerateLexerTask extends SourceTask { private File outputDir; @TaskAction public void generate() { List args = new ArrayList<>(); args.add("-d"); args.add(getOutputDir().getPath()); args.add("-q"); args.addAll(getSource().getFiles().stream().map(File::toString).collect(Collectors.toList())); try { Main.generate(args.toArray(new String[0])); } catch (SilentExit e) { throw new RuntimeException("JFlex generation failed", e); } } @OutputDirectory public File getOutputDir() { return outputDir; } public void setOutputDir(final File outputDir) { this.outputDir = outputDir; } } lightvalue-0.8.1/examples/000077500000000000000000000000001336500603600155045ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/000077500000000000000000000000001336500603600212355ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/README.md000066400000000000000000000000001336500603600225020ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/build.gradle000066400000000000000000000007261336500603600235210ustar00rootroot00000000000000buildscript { repositories { mavenCentral() } dependencies { classpath 'org.kravemir.lightvalue:lightvalue-gradle-plugin:0.8.1' } } apply plugin: 'java' apply plugin: 'org.kravemir.lightvalue' repositories { mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3' testCompile 'pl.pragmatists:JUnitParams:1.1.1' }lightvalue-0.8.1/examples/01-hello-value-model/src/000077500000000000000000000000001336500603600220245ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/000077500000000000000000000000001336500603600227505ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/java/000077500000000000000000000000001336500603600236715ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/java/org/000077500000000000000000000000001336500603600244605ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/java/org/kravemir/000077500000000000000000000000001336500603600263005ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/java/org/kravemir/lightvalue/000077500000000000000000000000001336500603600304445ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/java/org/kravemir/lightvalue/examples/000077500000000000000000000000001336500603600322625ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/java/org/kravemir/lightvalue/examples/hello/000077500000000000000000000000001336500603600333655ustar00rootroot00000000000000Main.java000066400000000000000000000027561336500603600350470ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/java/org/kravemir/lightvalue/examples/hellopackage org.kravemir.lightvalue.examples.hello; import java.io.PrintStream; public class Main { private static final String SEPARATOR = "============================================================"; private static void sendMessage(GreetingsMessage message, PrintStream output) { output.println(SEPARATOR); output.println("From: " + formatContactAddress(message.getFrom())); output.println("To: " + formatContactAddress(message.getTo())); output.println(SEPARATOR); output.println(String.format("Hello %s!", message.getTo().getName())); output.println(); output.println("Best wishes,"); output.println(message.getFrom().getName()); output.println(SEPARATOR); } private static String formatContactAddress(Contact contact) { return String.format("%s <%s>", contact.getName(), contact.getEmail()); } public static void main(String[] argv) { GreetingsMessage message = GreetingsMessage.newBuilder() .setFrom(Contact.newBuilder() .setName("LightValue Example01") .setEmail("something@somewhere.in.space") .build() ) .setTo(Contact.newBuilder() .setName("LightValue User") .setEmail("someone@earth.planet") .build() ) .build(); sendMessage(message, System.out); } } lightvalue-0.8.1/examples/01-hello-value-model/src/main/model/000077500000000000000000000000001336500603600240505ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/main/model/model.txt000066400000000000000000000002441336500603600257110ustar00rootroot00000000000000package org.kravemir.lightvalue.examples.hello; class Contact { string name; string email; } class GreetingsMessage { Contact from; Contact to; } lightvalue-0.8.1/examples/01-hello-value-model/src/test/000077500000000000000000000000001336500603600230035ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/test/java/000077500000000000000000000000001336500603600237245ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/test/java/org/000077500000000000000000000000001336500603600245135ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/test/java/org/kravemir/000077500000000000000000000000001336500603600263335ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/test/java/org/kravemir/lightvalue/000077500000000000000000000000001336500603600304775ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/test/java/org/kravemir/lightvalue/examples/000077500000000000000000000000001336500603600323155ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/test/java/org/kravemir/lightvalue/examples/hello/000077500000000000000000000000001336500603600334205ustar00rootroot00000000000000ContactTest.java000066400000000000000000000067351336500603600364520ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/test/java/org/kravemir/lightvalue/examples/hellopackage org.kravemir.lightvalue.examples.hello; import junitparams.JUnitParamsRunner; import junitparams.Parameters; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Objects; import java.util.function.Consumer; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @RunWith(JUnitParamsRunner.class) public class ContactTest { @Test public void testBuildGuineaPig() { Contact contact = buildGuineaPig(); assertThat(contact.getName(), is("Guinea Pig")); assertThat(contact.getEmail(), is("runner@local.cage")); } @Test public void testBuildLabRat() { Contact contact = buildLabRat(); assertThat(contact.getName(), is("Lab Rat")); assertThat(contact.getEmail(), is("test-subject@laboratory")); } @Test @Parameters public void testEquals(Contact a, Contact b, boolean expected) { assertThat(Objects.equals(a,b), is(expected)); } public Object[] parametersForTestEquals() { return new Object[][] { {buildGuineaPig(), buildGuineaPig(), true}, {buildLabRat(), buildLabRat(), true}, {buildGuineaPig(), buildLabRat(), false}, {buildLabRat(), buildGuineaPig(), false}, }; } @Test @Parameters public void testHashCode(Contact a, Contact b) { assertTrue(a.hashCode() == b.hashCode()); } public Object[] parametersForTestHashCode() { return new Object[][] { {buildGuineaPig(), buildGuineaPig()}, {buildLabRat(), buildLabRat()}, }; } @Test @Parameters public void testRebuild(Contact original) { Contact rebuilt = original.toBuilder().build(); assertThat(rebuilt.hashCode(), is(original.hashCode())); assertTrue(Objects.equals(original, rebuilt)); } public Object[] parametersForTestRebuild() { return new Object[][] { {buildGuineaPig()}, {buildLabRat()}, }; } @Test @Parameters public void testRebuildModify(Contact original, Consumer modifier) { Contact.Builder builder = original.toBuilder(); modifier.accept(builder); Contact rebuilt = builder.build(); assertFalse(Objects.equals(original, rebuilt)); } public Object[] parametersForTestRebuildModify() { return new Object[][] { {buildGuineaPig(), contactModifier(b -> b.setName("CORRUPTED"))}, {buildGuineaPig(), contactModifier(b -> b.setEmail("CORRUPTED"))}, {buildLabRat(), contactModifier(b -> b.setName("CORRUPTED"))}, {buildLabRat(), contactModifier(b -> b.setEmail("CORRUPTED"))}, }; } private Consumer contactModifier(Consumer modifier) { return modifier; } private Contact buildGuineaPig() { // NO CREATURE WAS HARMED in order to write this library return Contact.newBuilder() .setName("Guinea Pig") .setEmail("runner@local.cage") .build(); } private Contact buildLabRat() { // NO CREATURE WAS HARMED in order to write this library return Contact.newBuilder() .setName("Lab Rat") .setEmail("test-subject@laboratory") .build(); } } MainTest.java000066400000000000000000000024501336500603600357310ustar00rootroot00000000000000lightvalue-0.8.1/examples/01-hello-value-model/src/test/java/org/kravemir/lightvalue/examples/hellopackage org.kravemir.lightvalue.examples.hello; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import static org.hamcrest.Matchers.arrayContaining; import static org.junit.Assert.assertThat; public class MainTest { private static final String[] expectedOutput = new String[] { "============================================================", "From: LightValue Example01 ", "To: LightValue User ", "============================================================", "Hello LightValue User!", "", "Best wishes,", "LightValue Example01", "============================================================" }; @Test public void testMain() { final PrintStream originalOut = System.out; try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); Main.main(new String[]{}); String output = new String(outputStream.toByteArray()); assertThat(output.split("\n"), arrayContaining(expectedOutput)); } finally { System.setOut(originalOut); } } } lightvalue-0.8.1/examples/02-data-types/000077500000000000000000000000001336500603600177765ustar00rootroot00000000000000lightvalue-0.8.1/examples/02-data-types/README.md000066400000000000000000000001411336500603600212510ustar00rootroot00000000000000# Supported Data Types > **T O D O** For now, see [JSON (de)serialization example](../03-json).lightvalue-0.8.1/examples/03-json/000077500000000000000000000000001336500603600166755ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/README.md000066400000000000000000000000001336500603600201420ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/build.gradle000066400000000000000000000012711336500603600211550ustar00rootroot00000000000000buildscript { repositories { mavenCentral() } dependencies { classpath 'org.kravemir.lightvalue:lightvalue-gradle-plugin:0.8.1' } } apply plugin: 'java' apply plugin: 'org.kravemir.lightvalue' repositories { mavenCentral() } lightvalue.main.generateJacksonMixIns = true dependencies { compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.6' compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.6' testCompile group: 'junit', name: 'junit', version: '4.11' testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3' testCompile 'pl.pragmatists:JUnitParams:1.1.1' }lightvalue-0.8.1/examples/03-json/src/000077500000000000000000000000001336500603600174645ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/main/000077500000000000000000000000001336500603600204105ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/main/model/000077500000000000000000000000001336500603600215105ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/main/model/model.txt000066400000000000000000000010151336500603600233460ustar00rootroot00000000000000package org.kravemir.lightvalue.examples.json; class Contact { string name; string email; } class GreetingsMessage { Contact from; Contact to; } class TestModel { class InnerTest { string a; } class AnotherInner { string b; } bool b; int32 i; double d; string s; InnerTest inner1; InnerTest inner2; AnotherInner inner3; } class Empty { class WithInner { class WithInnerHavingProperty { string test; } } }lightvalue-0.8.1/examples/03-json/src/test/000077500000000000000000000000001336500603600204435ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/java/000077500000000000000000000000001336500603600213645ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/java/org/000077500000000000000000000000001336500603600221535ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/java/org/kravemir/000077500000000000000000000000001336500603600237735ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/java/org/kravemir/lightvalue/000077500000000000000000000000001336500603600261375ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/java/org/kravemir/lightvalue/examples/000077500000000000000000000000001336500603600277555ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/java/org/kravemir/lightvalue/examples/json/000077500000000000000000000000001336500603600307265ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/java/org/kravemir/lightvalue/examples/json/test/000077500000000000000000000000001336500603600317055ustar00rootroot00000000000000TestModelJsonDeserializationTest.java000066400000000000000000000036171336500603600411410ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/java/org/kravemir/lightvalue/examples/json/testpackage org.kravemir.lightvalue.examples.json.test; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.Test; import org.kravemir.lightvalue.examples.json.JacksonMixIns; import org.kravemir.lightvalue.examples.json.TestModel; import java.io.IOException; import java.net.URL; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; public class TestModelJsonDeserializationTest { private ObjectMapper objectMapper; @Before public void setUp() throws Exception { objectMapper = new ObjectMapper(); JacksonMixIns.registerMixIns(objectMapper); } @Test public void testDeserialization() throws IOException { // given URL resource = getClass().getResource("/test.json"); // when TestModel value = objectMapper.readValue( resource, TestModel.class ); // them assertThat( value, is( TestModel.newBuilder() .setB(true) .setI(42) .setD(3.14) .setS("A String") .setInner1( TestModel.InnerTest.newBuilder() .setA("string, a 1") .build() ) .setInner2( TestModel.InnerTest.newBuilder() .setA("string, a 2") .build() ) .setInner3( TestModel.AnotherInner.newBuilder() .setB("string, b 3") .build() ) .build() )); } } lightvalue-0.8.1/examples/03-json/src/test/resources/000077500000000000000000000000001336500603600224555ustar00rootroot00000000000000lightvalue-0.8.1/examples/03-json/src/test/resources/test.json000066400000000000000000000002711336500603600243270ustar00rootroot00000000000000{ "b": true, "i": 42, "d": 3.14, "s": "A String", "inner1": { "a": "string, a 1" }, "inner2": { "a": "string, a 2" }, "inner3": { "b": "string, b 3" } }lightvalue-0.8.1/examples/04-multiproject/000077500000000000000000000000001336500603600204465ustar00rootroot00000000000000lightvalue-0.8.1/examples/04-multiproject/README.md000066400000000000000000000000001336500603600217130ustar00rootroot00000000000000lightvalue-0.8.1/examples/settings.gradle000066400000000000000000000001571336500603600205270ustar00rootroot00000000000000rootProject.name = 'LightValue Examples' include '01-hello-value-model' include '03-json' includeBuild('..') lightvalue-0.8.1/gradle/000077500000000000000000000000001336500603600151245ustar00rootroot00000000000000lightvalue-0.8.1/gradle/publishing.gradle000066400000000000000000000023251336500603600204520ustar00rootroot00000000000000 ext.fillPom = { org.gradle.api.publish.maven.MavenPom pom, String name, String description -> pom.withXml { asNode().with { appendNode('name', name) appendNode('description', description) appendNode('url', 'https://gitlab.com/kravemir/lightvalue') appendNode('licenses').with { appendNode('license').with { appendNode('name', 'Apache License, Version 2.0') appendNode('url', 'https://www.apache.org/licenses/LICENSE-2.0.txt') } } appendNode('developers').with { appendNode('developer').with { appendNode('name', 'Miroslav Kravec') appendNode('email', 'kravec.miroslav@gmail.com') appendNode('organization', null) } } appendNode('scm').with { appendNode('connection', 'scm:git:git://gitlab.com/kravemir/lightvalue.git') appendNode('developerConnection', 'scm:git:ssh://git@gitlab.com:kravemir/lightvalue.git') appendNode('url', 'https://gitlab.com/kravemir/lightvalue/tree/master') } } } } lightvalue-0.8.1/lightvalue-generator/000077500000000000000000000000001336500603600200165ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/build.gradle000066400000000000000000000022251336500603600222760ustar00rootroot00000000000000import org.kravemir.lightvalue.buildsrc.GenerateLexerTask; dependencies { compile group: 'com.squareup', name: 'javapoet', version: '1.11.1' testCompile group: 'junit', name: 'junit', version: '4.11' testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3' testCompile 'pl.pragmatists:JUnitParams:1.1.1' } sourceSets { main { java { srcDir "${buildDir}/generated/lexer" } } } task generateLexer(type: GenerateLexerTask) { source file("src/jflex") outputDir file("${buildDir}/generated/lexer/org/kravemir/lightvalue") } compileJava.dependsOn(generateLexer) publishing { publications { libraryMaven(MavenPublication) { groupId project.group artifactId project.name version project.version from components.java artifact sourceJar artifact javadocJar project.ext.fillPom(pom, project.artifactName, project.artifactDescription) } } } idea { module { sourceDirs -= file("${buildDir}/generated/lexer") generatedSourceDirs += file("${buildDir}/generated/lexer") } } lightvalue-0.8.1/lightvalue-generator/gradle.properties000066400000000000000000000001731336500603600233730ustar00rootroot00000000000000artifactName=LightValue Generator Library artifactDescription=Java library to generate lightweight value classes for modelslightvalue-0.8.1/lightvalue-generator/src/000077500000000000000000000000001336500603600206055ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/jflex/000077500000000000000000000000001336500603600217155ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/jflex/grammar.flex000066400000000000000000000030111336500603600242160ustar00rootroot00000000000000package org.kravemir.lightvalue; %% %public %class ModelLexer %unicode %type Token %line LineTerminator = \r|\n|\r\n WhiteSpace = {LineTerminator} | [ \t\f] Identifier = [:jletter:] [:jletterdigit:]* %% /* keywords */ "package" { return new Token(Token.Type.PACKAGE, yyline); } "import" { return new Token(Token.Type.IMPORT, yyline); } "class" { return new Token(Token.Type.CLASS, yyline); } "repeated" { return new Token(Token.Type.REPEATED, yyline); } "map" { return new Token(Token.Type.MAP, yyline); } { /* identifiers */ {Identifier} { return new Token(Token.Type.IDENTIFIER, yyline, yytext()); } /* operators */ "." { return new Token(Token.Type.DOT, yyline); } "," { return new Token(Token.Type.COMMA, yyline); } ";" { return new Token(Token.Type.SEMICOLON, yyline); } "{" { return new Token(Token.Type.LCURLY, yyline); } "}" { return new Token(Token.Type.RCURLY, yyline); } "<" { return new Token(Token.Type.LESS, yyline); } ">" { return new Token(Token.Type.MORE, yyline); } /* whitespace */ {WhiteSpace} { /* ignore */ } } /* error fallback */ [^] { throw new RuntimeException("Illegal character <" +yytext() +">"); } lightvalue-0.8.1/lightvalue-generator/src/main/000077500000000000000000000000001336500603600215315ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/000077500000000000000000000000001336500603600224525ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/org/000077500000000000000000000000001336500603600232415ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/000077500000000000000000000000001336500603600250615ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/000077500000000000000000000000001336500603600272255ustar00rootroot00000000000000ClassModelGenerationContext.java000066400000000000000000000026771336500603600354340ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvaluepackage org.kravemir.lightvalue; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeName; import org.kravemir.lightvalue.model.ClassDef; public class ClassModelGenerationContext extends ModelGenerationContext { private final ModelGenerationContext parent; private final ClassDef classDef; private final ClassName classTypeName; private final ClassName builderTypeName; public ClassModelGenerationContext( final ModelGenerationContext parent, final ClassDef classDef, final ClassName classTypeName, final ClassName builderTypeName ) { this.parent = parent; this.classDef = classDef; this.classTypeName = classTypeName; this.builderTypeName = builderTypeName; } @Override public String getPackageName() { return parent.getPackageName(); } public ClassDef getClassDef() { return classDef; } public ClassName getClassTypeName() { return classTypeName; } public ClassName getBuilderTypeName() { return builderTypeName; } @Override public TypeName getType(String typeRawName) { TypeName name = super.getType(typeRawName); if(name == null) { name = parent.getType(typeRawName); } return name; } @Override protected String nameThisContext() { return "context for " + classDef.getName(); } } JacksonMixInsGenerator.java000066400000000000000000000131371336500603600344050ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvaluepackage org.kravemir.lightvalue; import com.squareup.javapoet.*; import org.kravemir.lightvalue.model.ClassDef; import org.kravemir.lightvalue.model.ModelFile; import javax.lang.model.element.Modifier; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; public class JacksonMixInsGenerator { private final static ClassName OBJECT_MAPPER_NAME = ClassName.get( "com.fasterxml.jackson.databind", "ObjectMapper" ); private final static ClassName JSON_DESERIALIZE_NAME = ClassName.get( "com.fasterxml.jackson.databind.annotation", "JsonDeserialize" ); private final static ClassName JSON_POJO_BUILDER_NAME = ClassName.get( "com.fasterxml.jackson.databind.annotation", "JsonPOJOBuilder" ); public void generate(ModelFile modelFile, File outputDir) throws IOException { TypeSpec topClassTypeSpec = TypeSpec .classBuilder("JacksonMixIns") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addTypes(generateMixInClasses(modelFile)) .addMethod(generateRegisterMixInsMethod(modelFile)) .build(); JavaFile javaFile = JavaFile.builder(modelFile.getFilePackage(), topClassTypeSpec).build(); javaFile.writeTo(outputDir); } private Iterable generateMixInClasses(ModelFile modelFile) { return generateMixInClasses( name -> ClassName.get(modelFile.getFilePackage(), name), name -> ClassName.get(modelFile.getFilePackage(), "JacksonMixIns", name), modelFile.getClassDefs() ); } private Iterable generateMixInClasses( final Function targetNameGenerator, final Function mixInSourceGenerator, final Collection classDefs ) { return classDefs.stream().map(classDef -> { final ClassName targetName = targetNameGenerator.apply(classDef.getName()); final ClassName mixInSource = mixInSourceGenerator.apply(classDef.getName() + "MixIn"); return TypeSpec .classBuilder(mixInSource) .addAnnotation(AnnotationSpec .builder(JSON_DESERIALIZE_NAME) .addMember( "builder", CodeBlock.of("$T.Builder.class", targetName) ) .build() ) .addModifiers(Modifier.STATIC, Modifier.ABSTRACT, Modifier.PUBLIC) .addType(generateBuilder()) .addTypes(generateMixInClasses( targetName::nestedClass, mixInSource::nestedClass, classDef.getSubClassDefs() )) .build(); }).collect(Collectors.toList()); } private TypeSpec generateBuilder() { return TypeSpec .classBuilder("Builder") .addAnnotation(AnnotationSpec .builder(JSON_POJO_BUILDER_NAME) .addMember("withPrefix", CodeBlock.of("\"set\"")) .build() ) .addModifiers(Modifier.STATIC, Modifier.ABSTRACT, Modifier.PUBLIC) .build(); } private MethodSpec generateRegisterMixInsMethod(ModelFile modelFile) { return MethodSpec .methodBuilder("registerMixIns") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(OBJECT_MAPPER_NAME, "objectMapper") .addCode( generateRegisterCodeBlocks( name -> ClassName.get(modelFile.getFilePackage(), name), name -> ClassName.get(modelFile.getFilePackage(), "JacksonMixIns", name), modelFile.getClassDefs() ).collect(CodeBlock.joining("\n")) ) .build(); } private Stream generateRegisterCodeBlocks( final Function targetNameGenerator, final Function mixInSourceGenerator, final Collection classDefs ) { return classDefs.stream().flatMap(classDef -> { final ClassName targetName = targetNameGenerator.apply(classDef.getName()); final ClassName mixInSource = mixInSourceGenerator.apply(classDef.getName() + "MixIn"); return Stream.concat( Stream.of( generateRegisterValueClass(targetName, mixInSource), generateRegisterBuilder(targetName, mixInSource) ), generateRegisterCodeBlocks( targetName::nestedClass, mixInSource::nestedClass, classDef.getSubClassDefs() ) ); }); } private CodeBlock generateRegisterValueClass(ClassName targetName, ClassName mixInSource) { return CodeBlock.of("objectMapper.addMixIn($T.class, $T.class);", targetName, mixInSource); } private CodeBlock generateRegisterBuilder(ClassName targetName, ClassName mixInSource) { return CodeBlock.of("objectMapper.addMixIn($T.Builder.class, $T.Builder.class);\n", targetName, mixInSource); } } ModelGenerationContext.java000066400000000000000000000024071336500603600344350ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvaluepackage org.kravemir.lightvalue; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import java.util.HashMap; import java.util.Map; public class ModelGenerationContext { private String packageName; private Map classes = new HashMap<>(); public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; } public TypeName getType(String typeRawName) { switch (typeRawName) { case "bool": return TypeName.BOOLEAN; case "int32": return TypeName.INT; case "double": return TypeName.DOUBLE; case "string": return ClassName.get(String.class); } return classes.get(typeRawName); } public void registerType(String name, TypeName subModelType) { if(classes.get(name) != null) { throw new RuntimeException(String.format("Type %s is already registered in: %s", name, nameThisContext())); } classes.put(name, subModelType); } protected String nameThisContext() { return "root context"; } } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/ModelGenerator.java000066400000000000000000000426571336500603600330150ustar00rootroot00000000000000package org.kravemir.lightvalue; import com.squareup.javapoet.*; import org.kravemir.lightvalue.model.ClassDef; import org.kravemir.lightvalue.model.ModelFile; import org.kravemir.lightvalue.model.PropertyDef; import org.kravemir.lightvalue.naming.DefaultNameProvider; import org.kravemir.lightvalue.naming.NameProvider; import javax.lang.model.element.Modifier; import java.io.File; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; public class ModelGenerator { private final NameProvider nameProvider; public ModelGenerator() { this(new DefaultNameProvider()); } public ModelGenerator(NameProvider nameProvider) { this.nameProvider = nameProvider; } public void generate(ModelFile modelFile, File outputDir) throws IOException { ModelGenerationContext context = new ModelGenerationContext(); context.setPackageName(modelFile.getFilePackage()); for(Map.Entry entry : modelFile.getImports().entrySet()) { context.registerType(entry.getKey(), ClassName.bestGuess(entry.getValue())); } for(ClassDef classDef : modelFile.getClassDefs()) { JavaFile javaFile = generateTopClassDef(context, classDef); context.registerType(classDef.getName(), ClassName.get(context.getPackageName(), classDef.getName())); javaFile.writeTo(outputDir); } } private JavaFile generateTopClassDef(ModelGenerationContext context, ClassDef classDef) { ClassName classTypeName = ClassName.get(context.getPackageName(), classDef.getName()); ClassModelGenerationContext classContext = new ClassModelGenerationContext( context, classDef, classTypeName, classTypeName.nestedClass("Builder") ); Collection subModelsSpecs = generateSubModels(classContext); TypeSpec typeSpec = TypeSpec .classBuilder(classDef.getName()) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addFields(generateFields(classContext, Modifier.PRIVATE, Modifier.FINAL)) .addMethod(generateConstructor(classContext)) .addMethods(generateGetters(classContext)) .addMethod(generateHashCode(classDef)) .addMethod(generateEquals(classTypeName, classDef)) .addMethod(generateToBuilderMethod(classContext)) .addMethods(generateNewBuilderMethods(classContext)) .addType(generateBuilder(classContext)) .addTypes(subModelsSpecs) .build(); return JavaFile.builder(context.getPackageName(), typeSpec).build(); } private Collection generateSubModels(ClassModelGenerationContext parentContext) { Collection classDefs = parentContext.getClassDef().getSubClassDefs(); if(classDefs == null || classDefs.isEmpty()) { return Collections.emptyList(); } List typeSpecs = new ArrayList<>(); for(ClassDef classDef : classDefs) { TypeSpec subModelType = generateSubModel(parentContext, classDef); typeSpecs.add(subModelType); } return typeSpecs; } private TypeSpec generateSubModel(ClassModelGenerationContext parentContext, ClassDef classDef) { ClassName classTypeName = parentContext.getClassTypeName().nestedClass(classDef.getName()); ClassModelGenerationContext classContext = new ClassModelGenerationContext( parentContext, classDef, classTypeName, classTypeName.nestedClass("Builder") ); parentContext.registerType(classDef.getName(), classTypeName); Collection subModelsSpecs = generateSubModels(classContext); TypeSpec typeSpec = TypeSpec .classBuilder(classDef.getName()) .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addFields(generateFields(classContext, Modifier.PRIVATE, Modifier.FINAL)) .addMethod(generateConstructor(classContext)) .addMethods(generateGetters(classContext)) .addMethod(generateHashCode(classDef)) .addMethod(generateEquals(classTypeName, classDef)) .addMethod(generateToBuilderMethod(classContext)) .addMethods(generateNewBuilderMethods(classContext)) .addType(generateBuilder(classContext)) .addTypes(subModelsSpecs) .build(); return typeSpec; } private MethodSpec generateConstructor(ClassModelGenerationContext context) { MethodSpec.Builder builder = MethodSpec .constructorBuilder() .addModifiers(Modifier.PRIVATE) .addParameter( ParameterSpec.builder( context.getBuilderTypeName(), "builder", Modifier.FINAL ).build() ); for(PropertyDef property : context.getClassDef().getProperties()) { builder.addStatement("this.$L = builder.$L", nameProvider.getVariableName(property), nameProvider.getVariableName(property)); } return builder.build(); } private Iterable generateFields(ClassModelGenerationContext context, Modifier... modifiers) { Collection properties = context.getClassDef().getProperties(); if(properties == null || properties.isEmpty()) { return Collections.emptyList(); } List typeSpecs = new ArrayList<>(); for(PropertyDef propertyDef : properties) { typeSpecs.add(toFieldSpec(context, propertyDef, modifiers)); } return typeSpecs; } private FieldSpec toFieldSpec(ModelGenerationContext context, PropertyDef propertyDef, Modifier... modifiers) { return FieldSpec.builder( getTypeForProperty(context, propertyDef), nameProvider.getVariableName(propertyDef), modifiers ).build(); } private Iterable generateGetters(ClassModelGenerationContext context) { Collection properties = context.getClassDef().getProperties(); if(properties == null || properties.isEmpty()) { return Collections.emptyList(); } List methodSpecs = new ArrayList<>(); for(PropertyDef propertyDef : properties) { methodSpecs.add(generateGetter(context, propertyDef)); } return methodSpecs; } private MethodSpec generateGetter(ModelGenerationContext context, PropertyDef propertyDef) { return MethodSpec .methodBuilder("get" + nameProvider.getAccessorName(propertyDef)) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(getTypeForProperty(context, propertyDef)) .addCode(CodeBlock.builder() .addStatement("return this.$L", nameProvider.getVariableName(propertyDef)) .build() ) .build(); } private MethodSpec generateEquals(ClassName classTypeName, ClassDef classDef) { return MethodSpec .methodBuilder("equals") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addParameter(Object.class, "o") .returns(TypeName.BOOLEAN) .addCode("if (this == o) return true;\n") .addCode("if (o == null || getClass() != o.getClass()) return false;\n") .addCode("$T other = ($T) o;\n", classTypeName, classTypeName) .addStatement( genReturnEqualsStatement(classDef), genReturnEqualsSubstitutions(classDef) ) .build(); } private String genReturnEqualsStatement(ClassDef classDef) { if(classDef.getProperties().isEmpty()) { return "return true"; } return classDef.getProperties().stream() .map(this::toComparisonExpression) .collect(Collectors.joining(" && \n", "return ", "")); } private String toComparisonExpression(PropertyDef propertyDef) { String variableName = nameProvider.getVariableName(propertyDef); if(isPrimitiveType(propertyDef)) { return String.format("this.%s == other.%s", variableName, variableName); } else { return String.format("$T.equals(this.%s,other.%s)", variableName, variableName); } } private Object[] genReturnEqualsSubstitutions(ClassDef classDef) { if(classDef.getProperties().isEmpty()) { return new Object[0]; } return classDef.getProperties().stream() .filter(p -> !isPrimitiveType(p)) .map(p -> Objects.class) .toArray(); } private boolean isPrimitiveType(PropertyDef propertyDef) { if(propertyDef.isMap() || propertyDef.isRepeated()) { return false; } switch (propertyDef.getType1()) { case "bool": case "int32": case "double": return true; case "string": default: return false; } } private MethodSpec generateHashCode(ClassDef classDef) { return MethodSpec .methodBuilder("hashCode") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(TypeName.INT) .addStatement( genReturnHashCodeStatement(classDef), genReturnHashCodeSubstitutions(classDef) ) .build(); } private String genReturnHashCodeStatement(ClassDef classDef) { return classDef.getProperties().stream() .map(p -> "$L") .collect(Collectors.joining(",", "return $T.hash(", ")")); } private Object[] genReturnHashCodeSubstitutions(ClassDef classDef) { List substitutions = new ArrayList<>(1 + classDef.getProperties().size()); substitutions.add(TypeName.get(Objects.class)); for (PropertyDef property : classDef.getProperties()) { substitutions.add(nameProvider.getVariableName(property)); } return substitutions.toArray(); } private MethodSpec generateToBuilderMethod(ClassModelGenerationContext context) { return MethodSpec .methodBuilder("toBuilder") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(context.getBuilderTypeName()) .addStatement("return new $T(this)", context.getBuilderTypeName()) .build(); } private Iterable generateNewBuilderMethods(ClassModelGenerationContext context) { return Collections.singletonList( MethodSpec .methodBuilder("newBuilder") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(context.getBuilderTypeName()) .addCode("return new $T();\n", context.getBuilderTypeName()) .build() ); } private TypeSpec generateBuilder(ClassModelGenerationContext context) { return TypeSpec .classBuilder("Builder") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .addFields(generateFields(context, Modifier.PRIVATE)) .addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).build()) .addMethod(generateCopyConstructor(context)) .addMethods(generateSetters(context)) .addMethod(generateBuildMethod(context.getClassTypeName())) .build(); } private MethodSpec generateCopyConstructor(ClassModelGenerationContext context) { MethodSpec.Builder builder = MethodSpec .constructorBuilder() .addModifiers(Modifier.PRIVATE) .addParameter( ParameterSpec.builder( context.getClassTypeName(), "original", Modifier.FINAL ).build() ); for(PropertyDef property : context.getClassDef().getProperties()) { builder.addStatement("this.$L = original.$L", nameProvider.getVariableName(property), nameProvider.getVariableName(property)); } return builder.build(); } private Iterable generateSetters(ClassModelGenerationContext context) { Collection properties = context.getClassDef().getProperties(); if(properties == null || properties.isEmpty()) { return Collections.emptyList(); } List methodSpecs = new ArrayList<>(); for(PropertyDef propertyDef : properties) { if(propertyDef.isRepeated()) { methodSpecs.addAll(generateAddMethods(context, propertyDef)); } else { methodSpecs.add(generateSetter(context, propertyDef)); } } return methodSpecs; } private MethodSpec generateSetter(ClassModelGenerationContext context, PropertyDef propertyDef) { return MethodSpec .methodBuilder("set" + nameProvider.getAccessorName(propertyDef)) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(context.getBuilderTypeName()) .addParameter(getTypeForProperty(context, propertyDef), "value", Modifier.FINAL) .addStatement("this.$L = value", nameProvider.getVariableName(propertyDef)) .addStatement("return this") .build(); } private Collection generateAddMethods(ClassModelGenerationContext context, PropertyDef propertyDef) { return Arrays.asList( MethodSpec .methodBuilder("set" + nameProvider.getAccessorName(propertyDef)) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(context.getBuilderTypeName()) .addParameter(getTypeForProperty(context, propertyDef), "value", Modifier.FINAL) .addStatement("this.$L = new $T<>(value)", nameProvider.getVariableName(propertyDef), ClassName.get(ArrayList.class)) .addStatement("return this") .build(), MethodSpec .methodBuilder("add" + nameProvider.getSingularAccessorName(propertyDef)) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(context.getBuilderTypeName()) .addParameter(context.getType(propertyDef.getType1()), "value", Modifier.FINAL) .addStatement( "if(this.$L == null) { this.$L = new $T<>(); }", nameProvider.getVariableName(propertyDef), nameProvider.getVariableName(propertyDef), ClassName.get(ArrayList.class) ) .addStatement("this.$L.add(value)", nameProvider.getVariableName(propertyDef)) .addStatement("return this") .build(), MethodSpec .methodBuilder("addAll" + nameProvider.getAccessorName(propertyDef)) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(context.getBuilderTypeName()) .addParameter(getTypeForProperty(context, propertyDef), "values", Modifier.FINAL) .addStatement( "if(this.$L == null) { this.$L = new $T<>(); }", nameProvider.getVariableName(propertyDef), nameProvider.getVariableName(propertyDef), ClassName.get(ArrayList.class) ) .addStatement("this.$L.addAll(values)", nameProvider.getVariableName(propertyDef)) .addStatement("return this") .build() ); } private MethodSpec generateBuildMethod(ClassName classTypeName) { return MethodSpec .methodBuilder("build") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(classTypeName) .addStatement("return new $T(this)", classTypeName) .build(); } private TypeName getTypeForProperty(ModelGenerationContext context, PropertyDef propertyDef) { if(propertyDef.isRepeated()) { return ParameterizedTypeName.get(ClassName.get(List.class), context.getType(propertyDef.getType1())); } else if(propertyDef.isMap()) { return ParameterizedTypeName.get( ClassName.get(Map.class), context.getType(propertyDef.getType1()), context.getType(propertyDef.getType2()) ); } else { return context.getType(propertyDef.getType1()); } } } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/ModelParser.java000066400000000000000000000104671336500603600323150ustar00rootroot00000000000000package org.kravemir.lightvalue; import org.kravemir.lightvalue.model.ClassDef; import org.kravemir.lightvalue.model.ModelFile; import org.kravemir.lightvalue.model.PropertyDef; import java.util.*; public class ModelParser { private final Queue tokens; public ModelParser(Collection tokens) { this.tokens = new ArrayDeque<>(tokens); } public ModelFile parseFile() { ModelFile file = new ModelFile(); consumeToken(Token.Type.PACKAGE); file.setFilePackage(parseIdentifierPath()); consumeToken(Token.Type.SEMICOLON); parseImports(file); parseFileStatements(file); return file; } private void parseImports(ModelFile file) { while(isNext(Token.Type.IMPORT)) { consumeToken(); file.addImport(parseIdentifierPath()); consumeToken(Token.Type.SEMICOLON); } } private void parseFileStatements(ModelFile file) { while(!tokens.isEmpty()) { parseFileStatement(file); } } private void parseFileStatement(ModelFile file) { file.addClass(parseClassDefinition()); } private ClassDef parseClassDefinition() { ClassDef classDef = new ClassDef(); consumeToken(Token.Type.CLASS); classDef.setName(consumeToken(Token.Type.IDENTIFIER).getValue()); parseClassStatements(classDef); return classDef; } private void parseClassStatements(ClassDef classDef) { consumeToken(Token.Type.LCURLY); while(!tokens.isEmpty()) { switch (nextType()) { case CLASS: ClassDef subClassDef = parseClassDefinition(); classDef.addSubClass(subClassDef); break; case IDENTIFIER: case REPEATED: case MAP: parseClassProperty(classDef); break; case RCURLY: consumeToken(); return; default: throw new RuntimeException(String.format("Unexpected token %s, expected class statement", nextType())); } } } private void parseClassProperty(ClassDef classDef) { String name; String type1 = null, type2 = null; boolean repeated = false; boolean map = false; switch (nextType()) { case REPEATED: repeated = true; consumeToken(); type1 = consumeToken(Token.Type.IDENTIFIER).getValue(); break; case MAP: map = true; consumeToken(); consumeToken(Token.Type.LESS); type1 = consumeToken(Token.Type.IDENTIFIER).getValue(); consumeToken(Token.Type.COMMA); type2 = consumeToken(Token.Type.IDENTIFIER).getValue(); consumeToken(Token.Type.MORE); break; case IDENTIFIER: type1 = consumeToken().getValue(); break; } name = consumeToken(Token.Type.IDENTIFIER).getValue(); consumeToken(Token.Type.SEMICOLON); classDef.addProperty(new PropertyDef( name, type1, type2, repeated, map )); } private String parseIdentifierPath() { List identifiers = new ArrayList<>(); identifiers.add(consumeToken(Token.Type.IDENTIFIER).getValue()); while(isNext(Token.Type.DOT)) { consumeToken(); identifiers.add(consumeToken(Token.Type.IDENTIFIER).getValue()); } return String.join(".", identifiers); } private Token.Type nextType() { return tokens.peek().getType(); } private boolean isNext(Token.Type tokenType) { return Objects.equals(nextType(), tokenType); } private Token consumeToken() { return tokens.poll(); } private Token consumeToken(Token.Type expectedTokenType) { if(isNext(expectedTokenType)) { return consumeToken(); } else { throw new RuntimeException(String.format( "Unexpected token %s at %d, expected %s", tokens.peek().getType(), tokens.peek().getLine(), expectedTokenType )); } } } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/Token.java000066400000000000000000000017321336500603600311530ustar00rootroot00000000000000package org.kravemir.lightvalue; public class Token { public enum Type { PACKAGE, IMPORT, CLASS, REPEATED, MAP, IDENTIFIER, DOT, COMMA, SEMICOLON, LCURLY, RCURLY, LESS, MORE, } private final Type type; private final int line; private final String value; public Token(Type type, int line) { this(type, line, null); } public Token(Type type, int line, String value) { this.type = type; this.line = line; this.value = value; } public Type getType() { return type; } public int getLine() { return line; } public String getValue() { return value; } @Override public String toString() { return "Token{" + "type=" + type + ", line=" + line + ", value='" + value + '\'' + '}'; } } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/model/000077500000000000000000000000001336500603600303255ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/model/ClassDef.java000066400000000000000000000026621336500603600326620ustar00rootroot00000000000000package org.kravemir.lightvalue.model; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; public class ClassDef { private String name; private Map subClasses = new LinkedHashMap<>(); private Map properties = new LinkedHashMap<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public Collection getSubClassDefs() { return subClasses.values(); } public Collection getProperties() { return properties.values(); } public void addSubClass(ClassDef classDef) { if(subClasses.get(classDef.getName()) != null) { throw new RuntimeException(String.format("Class %s already exists", classDef.getName())); } subClasses.put(classDef.getName(), classDef); } public void addProperty(PropertyDef propertyDef) { if(properties.get(propertyDef.getName()) != null) { throw new RuntimeException(String.format("Property %s already exists", propertyDef.getName())); } properties.put(propertyDef.getName(), propertyDef); } @Override public String toString() { return "ClassDef{" + "name='" + name + '\'' + ", subClasses=" + subClasses + ", properties=" + properties + '}'; } } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/model/ModelFile.java000066400000000000000000000031571336500603600330360ustar00rootroot00000000000000package org.kravemir.lightvalue.model; import java.util.*; public class ModelFile { private String filePackage; private Map imports = new LinkedHashMap<>(); private Map classDefMap = new LinkedHashMap<>(); public String getFilePackage() { return filePackage; } public void setFilePackage(String filePackage) { this.filePackage = filePackage; } public Map getImports() { return imports; } public void addImport(String path) { String[] splitPath = path.split("\\."); String importName = splitPath[splitPath.length-1]; if(imports.get(importName) != null) { throw new RuntimeException(String.format("Import for %s already exists", importName)); } imports.put(importName, path); } public Collection getClassDefs() { return classDefMap.values(); } public void addClass(ClassDef classDef) { if(classDefMap.get(classDef.getName()) != null) { throw new RuntimeException(String.format("Import for %s already exists, can't create class with same name", classDef.getName())); } if(classDefMap.get(classDef.getName()) != null) { throw new RuntimeException(String.format("Class %s already exists", classDef.getName())); } classDefMap.put(classDef.getName(), classDef); } @Override public String toString() { return "ModelFile{" + "filePackage='" + filePackage + '\'' + ", classDefMap=" + classDefMap + '}'; } } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/model/PropertyDef.java000066400000000000000000000021001336500603600334240ustar00rootroot00000000000000package org.kravemir.lightvalue.model; public class PropertyDef { private final String name; private final String type1; private final String type2; private final boolean repeated; private final boolean map; public PropertyDef(String name, String type1, String type2, boolean repeated, boolean map) { this.name = name; this.type1 = type1; this.type2 = type2; this.repeated = repeated; this.map = map; } public String getName() { return name; } public String getType1() { return type1; } public String getType2() { return type2; } public boolean isRepeated() { return repeated; } public boolean isMap() { return map; } @Override public String toString() { return "PropertyDef{" + "name='" + name + '\'' + ", type1='" + type1 + '\'' + ", type2='" + type2 + '\'' + ", repeated=" + repeated + ", map=" + map + '}'; } } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/naming/000077500000000000000000000000001336500603600304765ustar00rootroot00000000000000DefaultNameProvider.java000066400000000000000000000030011336500603600351540ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/namingpackage org.kravemir.lightvalue.naming; import org.kravemir.lightvalue.model.PropertyDef; import java.util.Arrays; import java.util.stream.Collectors; public class DefaultNameProvider implements NameProvider { private Pluralizer pluralizer = new Pluralizer(); @Override public String getAccessorName(PropertyDef propertyDef) { String accessor = toCamelCase(propertyDef.getName()); if(propertyDef.isRepeated()) { accessor = pluralizer.pluralize(accessor); } return accessor; } @Override public String getSingularAccessorName(PropertyDef propertyDef) { return toCamelCase(propertyDef.getName()); } @Override public String getVariableName(PropertyDef propertyDef) { String name = "m_" + firstToLowerCase(toCamelCase(propertyDef.getName())); if(propertyDef.isRepeated()) { if (name.charAt(name.length() -1) == 's') { name += "es"; } else { name += "s"; } } return name; } private static String toCamelCase(String s){ return Arrays.stream(s.split("_")) .map(DefaultNameProvider::firstToUpperCase) .collect(Collectors.joining("")); } private static String firstToLowerCase(String s) { return s.substring(0, 1).toLowerCase() + s.substring(1); } private static String firstToUpperCase(String s) { return s.substring(0, 1).toUpperCase() + s.substring(1); } } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/naming/NameProvider.java000066400000000000000000000004471336500603600337410ustar00rootroot00000000000000package org.kravemir.lightvalue.naming; import org.kravemir.lightvalue.model.PropertyDef; public interface NameProvider { String getAccessorName(PropertyDef propertyDef); String getSingularAccessorName(PropertyDef propertyDef); String getVariableName(PropertyDef propertyDef); } lightvalue-0.8.1/lightvalue-generator/src/main/java/org/kravemir/lightvalue/naming/Pluralizer.java000066400000000000000000000027671336500603600335060ustar00rootroot00000000000000package org.kravemir.lightvalue.naming; public class Pluralizer { private static final char[] CONSONANTS = new char[]{ }; public String pluralize(String singular) { if (endsWith(singular, "s", "x", "z", "ch", "sh")) { return singular + "es"; } if (endsWith(singular, "y") && isConsonant(singular, -2)) { return singular.substring(0, singular.length() - 1) + "ies"; } return singular + "s"; } private boolean isConsonant(String singular, int position) { if (position < 0) { position += singular.length(); } return isConsonant(singular.charAt(position)); } private boolean isConsonant(char c) { switch (c) { case 'b': case 'c': case 'd': case 'f': case 'g': case 'h': case 'j': case 'k': case 'l': case 'm': case 'n': case 'p': case 'q': case 'r': case 's': case 't': case 'v': case 'w': case 'x': case 'z': return true; default: return false; } } private boolean endsWith(String singular, String... postfixes) { for (String postfix : postfixes) { if (singular.endsWith(postfix)) { return true; } } return false; } } lightvalue-0.8.1/lightvalue-generator/src/test/000077500000000000000000000000001336500603600215645ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/test/java/000077500000000000000000000000001336500603600225055ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/test/java/org/000077500000000000000000000000001336500603600232745ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/test/java/org/kravemir/000077500000000000000000000000001336500603600251145ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/test/java/org/kravemir/lightvalue/000077500000000000000000000000001336500603600272605ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/test/java/org/kravemir/lightvalue/naming/000077500000000000000000000000001336500603600305315ustar00rootroot00000000000000PluralizerTest.java000066400000000000000000000024311336500603600343060ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-generator/src/test/java/org/kravemir/lightvalue/namingpackage org.kravemir.lightvalue.naming; import junitparams.JUnitParamsRunner; import junitparams.Parameters; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; @RunWith(JUnitParamsRunner.class) public class PluralizerTest { private Pluralizer pluralizer; @Before public void setUp() throws Exception { pluralizer = new Pluralizer(); } @Test @Parameters public void testPluralize(String singular, String expectedPlural) { assertThat(pluralizer.pluralize(singular), is(expectedPlural)); } public Object[] parametersForTestPluralize() { return new Object[][]{ // regular appending: -s {"boat", "boats"}, {"house", "houses"}, {"cat", "cats"}, {"river", "rivers"}, // regular appending: -es {"bus", "buses"}, {"wish", "wishes"}, {"pitch", "pitches"}, {"box", "boxes"}, {"penny", "pennies"}, {"spy", "spies"}, {"baby", "babies"}, {"city", "cities"}, {"daisy", "daisies"} }; } }lightvalue-0.8.1/lightvalue-gradle-plugin/000077500000000000000000000000001336500603600205625ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/build.gradle000066400000000000000000000014701336500603600230430ustar00rootroot00000000000000apply plugin: 'java-gradle-plugin' dependencies { compile project(':lightvalue-generator') } gradlePlugin { plugins { lightvalue { id = 'org.kravemir.lightvalue' implementationClass = 'org.kravemir.lightvalue.LightValuePlugin' } } } if (publishingEnabled) { afterEvaluate { publishing.publications { pluginMaven { artifact sourceJar artifact javadocJar project.ext.fillPom(pom, project.getProperty('artifactName'), project.getProperty('artifactDescription')) } lightvaluePluginMarkerMaven { project.ext.fillPom(pom, project.getProperty('pluginmarker.artifactName'), project.getProperty('pluginmarker.artifactDescription')) } } } }lightvalue-0.8.1/lightvalue-gradle-plugin/gradle.properties000066400000000000000000000004411336500603600241350ustar00rootroot00000000000000artifactName=LightValue Gradle Plugin artifactDescription=Gradle plugin to generate lightweight value classes for models pluginmarker.artifactName=LightValue Gradle Plugin Marker pluginmarker.artifactDescription=LightValue Gradle Plugin Marker to enable use inside `plugins {}` DSL block lightvalue-0.8.1/lightvalue-gradle-plugin/src/000077500000000000000000000000001336500603600213515ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/000077500000000000000000000000001336500603600222755ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/java/000077500000000000000000000000001336500603600232165ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/java/org/000077500000000000000000000000001336500603600240055ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/java/org/kravemir/000077500000000000000000000000001336500603600256255ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/java/org/kravemir/lightvalue/000077500000000000000000000000001336500603600277715ustar00rootroot00000000000000LightValueBasePlugin.java000066400000000000000000000023501336500603600345730ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/java/org/kravemir/lightvaluepackage org.kravemir.lightvalue; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.plugins.JavaBasePlugin; /** * Base plugin provides Light Value capabilities (functionality). * * Recommended usage is via {@link LightValuePlugin}, which setups configuration based on conventions. * * @see * Designing Gradle plugins: Capabilities vs. conventions */ public class LightValueBasePlugin implements Plugin { @Override public void apply(final Project project) { project.getPlugins().apply(JavaBasePlugin.class); NamedDomainObjectContainer lightValueContainer = project.container( LightValueConfiguration.class, name -> { LightValueGeneratorTask task = project.getTasks().create(name + "ValueClasses", LightValueGeneratorTask.class); task.setGroup("build"); return new LightValueConfiguration(name, task); } ); project.getExtensions().add("lightvalue", lightValueContainer); } } LightValueConfiguration.java000066400000000000000000000017771336500603600353650ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/java/org/kravemir/lightvaluepackage org.kravemir.lightvalue; import org.gradle.api.tasks.SourceSet; import java.io.File; public class LightValueConfiguration { private final String name; private final LightValueGeneratorTask task; public LightValueConfiguration(String name, LightValueGeneratorTask task) { this.name = name; this.task = task; } public String getName() { return name; } public LightValueGeneratorTask getTask() { return task; } public boolean getGenerateJacksonMixIns() { return task.getGenerateJacksonMixIns(); } public void setGenerateJacksonMixIns(boolean generateJacksonMixIns) { task.setGenerateJacksonMixIns(generateJacksonMixIns); } public void registerInSourceSets(SourceSet... sourceSets) { task.registerInSourceSets(sourceSets); } public void source(File srcDir) { task.source(srcDir); } public void setOutputDir(File outputDir) { task.setOutputDir(outputDir); } } LightValueGeneratorTask.java000066400000000000000000000063311336500603600353160ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/java/org/kravemir/lightvaluepackage org.kravemir.lightvalue; import org.gradle.api.Task; import org.gradle.api.tasks.*; import org.kravemir.lightvalue.model.ModelFile; import java.io.*; import java.util.ArrayList; import java.util.List; public class LightValueGeneratorTask extends SourceTask { private File outputDir; private boolean generateJacksonMixIns; @TaskAction public void generate() { prepareEmptyOutputDir(); getSource().getFiles().forEach(this::generate); } private void generate(File inputFile) { Reader reader = null; Writer writer = null; try { reader = new BufferedReader(new FileReader(inputFile)); writer = new BufferedWriter(new FileWriter(new File(getOutputDir(), inputFile.getName()))); ModelLexer modelLexer = new ModelLexer(reader); List tokens = parseTokens(modelLexer); writer.write(tokens.toString()); writer.write("\n"); writer.flush(); ModelFile modelFile = new ModelParser(tokens).parseFile(); writer.write(modelFile.toString()); writer.write("\n"); ModelGenerator modelGenerator = new ModelGenerator(); modelGenerator.generate(modelFile, getOutputDir()); if(generateJacksonMixIns) { JacksonMixInsGenerator jacksonMixInsGenerator = new JacksonMixInsGenerator(); jacksonMixInsGenerator.generate(modelFile, getOutputDir()); } } catch (IOException e) { throw new RuntimeException(e); } finally { closeIfOpen(reader); closeIfOpen(writer); } } private List parseTokens(ModelLexer modelLexer) throws IOException { List tokens = new ArrayList<>(); Token token; while ((token = modelLexer.yylex()) != null) { tokens.add(token); } return tokens; } private void closeIfOpen(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException e) { e.printStackTrace(System.err); } } private void prepareEmptyOutputDir() { if(getOutputDir().exists()) { getProject().delete(getOutputDir()); } getProject().mkdir(getOutputDir()); } @OutputDirectory public File getOutputDir() { return outputDir; } public void setOutputDir(final File outputDir) { this.outputDir = outputDir; } public void setOutputDir(final CharSequence outputDir) { this.outputDir = getProject().file(outputDir); } @Input public boolean getGenerateJacksonMixIns() { return generateJacksonMixIns; } public void setGenerateJacksonMixIns(boolean generateJacksonMixIns) { this.generateJacksonMixIns = generateJacksonMixIns; } public void registerInSourceSets(SourceSet... sourceSets) { for(SourceSet sourceSet : sourceSets) { sourceSet.getJava().srcDir(getOutputDir()); Task compileJavaTask = getProject().getTasks().getByPath(sourceSet.getCompileJavaTaskName()); compileJavaTask.dependsOn(this); } } } LightValuePlugin.java000066400000000000000000000043171336500603600340050ustar00rootroot00000000000000lightvalue-0.8.1/lightvalue-gradle-plugin/src/main/java/org/kravemir/lightvaluepackage org.kravemir.lightvalue; import org.gradle.api.*; import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; import java.io.File; /** * {@link LightValuePlugin} provides setups LightValue functionality with conventions. * * To use without default conventions, use {@link LightValueBasePlugin}. * * @see * Designing Gradle plugins: Capabilities vs. conventions */ public class LightValuePlugin implements Plugin { @Override public void apply(final Project project) { project.getPlugins().apply(LightValueBasePlugin.class); project.getPlugins().withType(JavaBasePlugin.class, javaBasePlugin -> { final NamedDomainObjectContainer lightValueContainer = getContainer(project); JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class); java.getSourceSets().all(sourceSet -> createConfigurationForSourceSet(lightValueContainer, sourceSet)); }); } private void createConfigurationForSourceSet( NamedDomainObjectContainer lightValueContainer, SourceSet sourceSet ) { final String name = sourceSet.getName(); LightValueConfiguration configuration = lightValueContainer.create(name); Project project = configuration.getTask().getProject(); File srcDir = project.file(String.format("%s/src/%s/model", project.getProjectDir().getPath(), name)); File outDir = project.file(String.format("%s/generated/lightValue/%s", project.getBuildDir().getPath(), name)); configuration.source(srcDir); configuration.setOutputDir(outDir); configuration.registerInSourceSets(sourceSet); } @SuppressWarnings("unchecked") // TODO: get rid of this, use some non-generic class / way to get it public NamedDomainObjectContainer getContainer(Project project) { return (NamedDomainObjectContainer) project.getExtensions().getByName("lightvalue"); } } lightvalue-0.8.1/settings.gradle000066400000000000000000000001441336500603600167050ustar00rootroot00000000000000rootProject.name = 'LightValue' include('lightvalue-generator') include('lightvalue-gradle-plugin')