├── .github └── workflows │ ├── branch-verify.yml │ └── deploy-main.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── pom.xml ├── renovate.json ├── spi-impl ├── pom.xml └── src │ └── main │ ├── java │ └── no │ │ └── entur │ │ └── mapstruct │ │ └── spi │ │ └── protobuf │ │ ├── ProcessingEnvOptionsHolder.java │ │ ├── ProtobufAccessorNamingStrategy.java │ │ ├── ProtobufEnumMappingStrategy.java │ │ └── ProtobufLiteBuilderProvider.java │ └── resources │ └── META-INF │ └── services │ ├── javax.annotation.processing.Processor │ ├── org.mapstruct.ap.spi.AccessorNamingStrategy │ ├── org.mapstruct.ap.spi.BuilderProvider │ └── org.mapstruct.ap.spi.EnumMappingStrategy ├── support-core ├── pom.xml └── src │ ├── main │ └── java │ │ └── no │ │ └── entur │ │ └── abt │ │ └── mapstruct │ │ └── common │ │ ├── ProtobufStandardMappings.java │ │ └── Timestamps.java │ └── test │ └── java │ └── no │ └── entur │ └── abt │ └── mapstruct │ └── common │ └── ProtobufStandardMappingsTest.java ├── support-lite ├── pom.xml └── src │ ├── main │ └── java │ │ └── no │ │ └── entur │ │ └── abt │ │ └── mapstruct │ │ └── ProtobufStandardMappings.java │ └── test │ └── java │ └── no │ └── entur │ └── abt │ └── mapstruct │ ├── Durations.java │ └── ProtobufStandardMappingsTest.java ├── support-standard ├── pom.xml └── src │ ├── main │ └── java │ │ └── no │ │ └── entur │ │ └── abt │ │ └── mapstruct │ │ └── ProtobufStandardMappings.java │ └── test │ └── java │ └── no │ └── entur │ └── abt │ └── mapstruct │ └── ProtobufStandardMappingsTest.java └── usage ├── pom.xml └── src ├── main ├── java │ └── no │ │ └── entur │ │ └── mapstruct │ │ ├── example │ │ ├── EnumPostfixOverrideMapper.java │ │ └── UserMapper.java │ │ └── spi │ │ └── protobuf │ │ ├── Department.java │ │ ├── EnumPostfixOverrideValues.java │ │ ├── House.java │ │ ├── MultiNumber.java │ │ ├── Permission.java │ │ ├── Status.java │ │ └── User.java └── proto │ ├── EnumPostfixOverride.proto │ └── User.proto └── test └── java └── no └── entur └── mapstruct └── example └── MappingTest.java /.github/workflows/branch-verify.yml: -------------------------------------------------------------------------------- 1 | name: Validate branch 2 | on: 3 | pull_request: 4 | types: 5 | - synchronize 6 | - opened 7 | 8 | jobs: 9 | validate-maven-build: 10 | uses: entur/abt-gha-public/.github/workflows/validate-jar-maven-sona.yml@main 11 | secrets: inherit 12 | with: 13 | server-id: entur-partner 14 | java-version: 18 15 | -------------------------------------------------------------------------------- /.github/workflows/deploy-main.yml: -------------------------------------------------------------------------------- 1 | name: Validate, build and deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | maven-build-and-release: 9 | uses: entur/abt-gha-public/.github/workflows/maven-release-sona.yml@main 10 | secrets: inherit 11 | with: 12 | server-id: ossrh 13 | channel-id: ${{ vars.CHANNEL_ID }} 14 | java-version: 18 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | **/target/ 3 | 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | EUROPEAN UNION PUBLIC LICENCE v. 1.2 2 | EUPL © the European Union 2007, 2016 3 | 4 | This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined below) which is provided under the 5 | terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such 6 | use is covered by a right of the copyright holder of the Work). 7 | The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following 8 | notice immediately following the copyright notice for the Work: 9 | Licensed under the EUPL 10 | or has expressed by any other means his willingness to license under the EUPL. 11 | 12 | 1.Definitions 13 | In this Licence, the following terms have the following meaning: 14 | — ‘The Licence’:this Licence. 15 | — ‘The Original Work’:the work or software distributed or communicated by the Licensor under this Licence, available 16 | as Source Code and also as Executable Code as the case may be. 17 | — ‘Derivative Works’:the works or software that could be created by the Licensee, based upon the Original Work or 18 | modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work 19 | required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in 20 | the country mentioned in Article 15. 21 | — ‘The Work’:the Original Work or its Derivative Works. 22 | — ‘The Source Code’:the human-readable form of the Work which is the most convenient for people to study and 23 | modify. 24 | — ‘The Executable Code’:any code which has generally been compiled and which is meant to be interpreted by 25 | a computer as a program. 26 | — ‘The Licensor’:the natural or legal person that distributes or communicates the Work under the Licence. 27 | — ‘Contributor(s)’:any natural or legal person who modifies the Work under the Licence, or otherwise contributes to 28 | the creation of a Derivative Work. 29 | — ‘The Licensee’ or ‘You’:any natural or legal person who makes any usage of the Work under the terms of the 30 | Licence. 31 | — ‘Distribution’ or ‘Communication’:any act of selling, giving, lending, renting, distributing, communicating, 32 | transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential 33 | functionalities at the disposal of any other natural or legal person. 34 | 35 | 2.Scope of the rights granted by the Licence 36 | The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for 37 | the duration of copyright vested in the Original Work: 38 | — use the Work in any circumstance and for all usage, 39 | — reproduce the Work, 40 | — modify the Work, and make Derivative Works based upon the Work, 41 | — communicate to the public, including the right to make available or display the Work or copies thereof to the public 42 | and perform publicly, as the case may be, the Work, 43 | — distribute the Work or copies thereof, 44 | — lend and rent the Work or copies thereof, 45 | — sublicense rights in the Work or copies thereof. 46 | Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the 47 | applicable law permits so. 48 | In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed 49 | by law in order to make effective the licence of the economic rights here above listed. 50 | The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the 51 | extent necessary to make use of the rights granted on the Work under this Licence. 52 | 53 | 3.Communication of the Source Code 54 | The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as 55 | Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with 56 | each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to 57 | the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to 58 | distribute or communicate the Work. 59 | 60 | 4.Limitations on copyright 61 | Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the 62 | exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations 63 | thereto. 64 | 65 | 5.Obligations of the Licensee 66 | The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those 67 | obligations are the following: 68 | 69 | Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to 70 | the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the 71 | Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work 72 | to carry prominent notices stating that the Work has been modified and the date of modification. 73 | 74 | Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this 75 | Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless 76 | the Original Work is expressly distributed only under this version of the Licence — for example by communicating 77 | ‘EUPL v. 1.2 only’. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the 78 | Work or Derivative Work that alter or restrict the terms of the Licence. 79 | 80 | Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both 81 | the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done 82 | under the terms of this Compatible Licence. For the sake of this clause, ‘Compatible Licence’ refers to the licences listed 83 | in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with 84 | his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. 85 | 86 | Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide 87 | a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available 88 | for as long as the Licensee continues to distribute or communicate the Work. 89 | Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names 90 | of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and 91 | reproducing the content of the copyright notice. 92 | 93 | 6.Chain of Authorship 94 | The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or 95 | licensed to him/her and that he/she has the power and authority to grant the Licence. 96 | Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or 97 | licensed to him/her and that he/she has the power and authority to grant the Licence. 98 | Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions 99 | to the Work, under the terms of this Licence. 100 | 101 | 7.Disclaimer of Warranty 102 | The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work 103 | and may therefore contain defects or ‘bugs’ inherent to this type of development. 104 | For the above reason, the Work is provided under the Licence on an ‘as is’ basis and without warranties of any kind 105 | concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or 106 | errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this 107 | Licence. 108 | This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. 109 | 110 | 8.Disclaimer of Liability 111 | Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be 112 | liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the 113 | Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss 114 | of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, 115 | the Licensor will be liable under statutory product liability laws as far such laws apply to the Work. 116 | 117 | 9.Additional agreements 118 | While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services 119 | consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole 120 | responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, 121 | defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by 122 | the fact You have accepted any warranty or additional liability. 123 | 124 | 10.Acceptance of the Licence 125 | The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ placed under the bottom of a window 126 | displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of 127 | applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms 128 | and conditions. 129 | Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You 130 | by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution 131 | or Communication by You of the Work or copies thereof. 132 | 133 | 11.Information to the public 134 | In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, 135 | by offering to download the Work from a remote location) the distribution channel or media (for example, a website) 136 | must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence 137 | and the way it may be accessible, concluded, stored and reproduced by the Licensee. 138 | 139 | 12.Termination of the Licence 140 | The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms 141 | of the Licence. 142 | Such a termination will not terminate the licences of any person who has received the Work from the Licensee under 143 | the Licence, provided such persons remain in full compliance with the Licence. 144 | 145 | 13.Miscellaneous 146 | Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the 147 | Work. 148 | If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or 149 | enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid 150 | and enforceable. 151 | The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of 152 | the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. 153 | New versions of the Licence will be published with a unique version number. 154 | All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take 155 | advantage of the linguistic version of their choice. 156 | 157 | 14.Jurisdiction 158 | Without prejudice to specific agreement between parties, 159 | — any litigation resulting from the interpretation of this License, arising between the European Union institutions, 160 | bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice 161 | of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union, 162 | — any litigation arising between other parties and resulting from the interpretation of this License, will be subject to 163 | the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. 164 | 165 | 15.Applicable Law 166 | Without prejudice to specific agreement between parties, 167 | — this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, 168 | resides or has his registered office, 169 | — this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside 170 | a European Union Member State. 171 | 172 | 173 | Appendix 174 | 175 | ‘Compatible Licences’ according to Article 5 EUPL are: 176 | — GNU General Public License (GPL) v. 2, v. 3 177 | — GNU Affero General Public License (AGPL) v. 3 178 | — Open Software License (OSL) v. 2.1, v. 3.0 179 | — Eclipse Public License (EPL) v. 1.0 180 | — CeCILL v. 2.0, v. 2.1 181 | — Mozilla Public Licence (MPL) v. 2 182 | — GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 183 | — Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software 184 | — European Union Public Licence (EUPL) v. 1.1, v. 1.2 185 | — Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+). 186 | 187 | The European Commission may update this Appendix to later versions of the above licences without producing 188 | a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the 189 | covered Source Code from exclusive appropriation. 190 | All other changes or additions to this Appendix require the production of a new EUPL version. 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mapstruct SPI implementation for protocol buffers [![CircleCI](https://circleci.com/gh/entur/mapstruct-spi-protobuf.svg?style=svg)](https://circleci.com/gh/entur/mapstruct-spi-protobuf) 2 | 3 | This naming strategy helps [mapstruct](http://mapstruct.org/) generate working mapping code between your domain classes 4 | and protobuf classes. Both [fullblown Java protobuf](https://github.com/protocolbuffers/protobuf/tree/master/java) 5 | and [protolite](https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md) classes suported. 6 | 7 | Requires on mapstruct 1.4.0+. 8 | 9 | ## ProtobufAccessorNamingStrategy 10 | 11 | Extends ```DefaultProtobufAccessorNamingStrategy``` and provides necessary information to map all fields automatically * 12 | except* 13 | 14 | * oneof 15 | 16 | which require manual mapping. 17 | 18 | ## ProtobufEnumMappingStrategy 19 | 20 | Implements ```EnumMappingStrategy``` and provides complete enum constant mappings if you follow Googles style guide for 21 | enums https://developers.google.com/protocol-buffers/docs/style#enums 22 | 23 | If needed you can specify a different postfix for the 0 value enum by passing in `mapstructSpi.enumPostfixOverrides` as 24 | a compilerArg in the format of: 25 | 26 | `-AmapstructSpi.enumPostfixOverrides=com.package.root.a=POSTFIX_1,com.package.root.b=POSTFIX_2` 27 | 28 | Otherwise, this will default to `UNSPECIFIED` as per the Google style guide. 29 | 30 | ```xml 31 | 32 | 33 | maven-compiler-plugin 34 | 35 | 36 | 37 | no.entur.mapstruct.spi 38 | protobuf-spi-impl 39 | LATEST.VERSION 40 | 41 | 42 | 43 | -AmapstructSpi.enumPostfixOverrides=com.company.name=INVALID 44 | 45 | 46 | 47 | 48 | ``` 49 | 50 | ## Support - Mapping functions: 51 | 52 | Standard mapping functions between often used proto types and java types: 53 | 54 | * Timestamp <-> Instant 55 | * Duration <-> Duration 56 | * Date <-> LocalDate 57 | * TimeOfDay <-> LocalTime 58 | * byte[] <-> ByteString 59 | 60 | See [protobuf-support-standard](support-standard) and/or [protobuf-support-lite](support-lite) folders for a 61 | ready-to-use mapstruct mapper. 62 | 63 | # Usage 64 | 65 | [See example project](usage/) 66 | 67 | NB: Make sure you add `collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED` to your mapping interfaces 68 | as protobuf stubs use the builder pattern. 69 | 70 | ``` 71 | @Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) 72 | public interface ... { 73 | ``` 74 | 75 | ## Maven 76 | 77 | NB: Make sure you use the *same version of mapstruct* both in the annotation process and the general dependency. 78 | `mapstruct-spi-protobuf` generally depends on the latest released version of mapstruct. 79 | 80 | Add the following section to you maven-compiler-plugin plugin configuration: 81 | 82 | ```xml 83 | 84 | 85 | 86 | no.entur.mapstruct.spi 87 | protobuf-spi-impl 88 | LATEST.VERSION 89 | 90 | 91 | 92 | 93 | org.mapstruct 94 | mapstruct 95 | ${org.mapstruct.version} 96 | 97 | 98 | 99 | ``` 100 | 101 | Complete example: 102 | 103 | ```xml 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-compiler-plugin 108 | 109 | 110 | 1.8 111 | 1.8 112 | 113 | 114 | no.entur.mapstruct.spi 115 | protobuf-spi-impl 116 | LATEST.VERSION 117 | 118 | 119 | 120 | 121 | 122 | org.mapstruct 123 | mapstruct 124 | ${org.mapstruct.version} 125 | 126 | 127 | 128 | 129 | ``` 130 | 131 | ## Gradle 132 | 133 | Note: See Maven setup regarding using the same version of mapstruct both in the annotation processor and the general 134 | dependency. 135 | 136 | ```java 137 | implementation"org.mapstruct:mapstruct:${mapstructVersion}" 138 | annotationProcessor"org.mapstruct:mapstruct-processor:${mapstructVersion}" 139 | annotationProcessor"no.entur.mapstruct.spi:protobuf-spi-impl:LATEST.VERSION" 140 | ``` 141 | 142 | # More information: 143 | 144 | http://mapstruct.org/documentation/stable/reference/html/index.html#using-spi 145 | 146 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | no.entur.mapstruct.spi 5 | spi-protobuf-parent 6 | 0.1-SNAPSHOT 7 | pom 8 | Mapstruct SPI implementation for protobuf stubs 9 | Protobuf support for mapstruct 10 | 11 | spi-impl 12 | usage 13 | support-core 14 | support-standard 15 | support-lite 16 | 17 | 18 | 2.51.0 19 | 2.12.1 20 | 33.4.0-jre 21 | 5.11.4 22 | 3.11.2 23 | 3.3.1 24 | 1.8 25 | 1.8 26 | 27 | 1.6.3 28 | 1.7.1 29 | UTF-8 30 | 3.25.6 31 | 0.7.1 32 | 2.0.16 33 | 2.44.2 34 | 35 | 36 | 37 | 38 | no.entur.mapstruct.spi 39 | protobuf-support-core 40 | ${project.version} 41 | 42 | 43 | com.google.api.grpc 44 | proto-google-common-protos 45 | ${grpc.api.version} 46 | 47 | 48 | com.google.protobuf 49 | protobuf-java 50 | ${proto.version} 51 | 52 | 53 | com.google.guava 54 | guava 55 | ${guava.version} 56 | 57 | 58 | org.mapstruct 59 | mapstruct 60 | ${org.mapstruct.version} 61 | 62 | 63 | org.mapstruct 64 | mapstruct-processor 65 | ${org.mapstruct.version} 66 | 67 | 68 | org.slf4j 69 | slf4j-api 70 | ${slf4j.version} 71 | 72 | 73 | 74 | com.google.code.gson 75 | gson 76 | ${gson.version} 77 | 78 | 79 | org.junit.jupiter 80 | junit-jupiter-engine 81 | ${jupiter.version} 82 | test 83 | 84 | 85 | org.junit.jupiter 86 | junit-jupiter-api 87 | ${jupiter.version} 88 | test 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-compiler-plugin 98 | 3.13.0 99 | 100 | ${maven.compiler.source} 101 | ${maven.compiler.target} 102 | 103 | 104 | 105 | com.diffplug.spotless 106 | spotless-maven-plugin 107 | ${spotless.version} 108 | 109 | 110 | 111 | eclipsecodestyle/abt_codestyle.xml 112 | 4.26 113 | 114 | 115 | 116 | 117 | eclipsecodestyle/abt.importorder 118 | 119 | 120 | 121 | UTF-8 122 | 123 | 124 | 125 | org.apache.maven.plugins 126 | maven-surefire-plugin 127 | 3.5.2 128 | 129 | false 130 | 131 | **/*IT.java 132 | 133 | 134 | 135 | 136 | org.junit.jupiter 137 | junit-jupiter-engine 138 | ${jupiter.version} 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | org.codehaus.mojo 147 | license-maven-plugin 148 | 2.5.0 149 | 150 | Entur 151 | 2019 152 | LICENSE.txt 153 | eupl_v1_1 154 | 155 | **/*.java 156 | 157 | 158 | 159 | 160 | check-license-header 161 | 162 | update-file-header 163 | 164 | initialize 165 | 166 | 167 | 168 | 169 | com.github.ekryd.sortpom 170 | sortpom-maven-plugin 171 | 4.0.0 172 | 173 | custom_1 174 | 4 175 | \n 176 | true 177 | false 178 | false 179 | scope 180 | 181 | 182 | 183 | 184 | sort 185 | 186 | initialize 187 | 188 | 189 | 190 | 191 | 192 | https://github.com/entur/mapstruct-spi-protobuf 193 | 2018 194 | 195 | Entur AS and original authors 196 | http://www.entur.org/ 197 | 198 | 199 | 200 | EUPL-1.2 with modifications 201 | https://joinup.ec.europa.eu/software/page/eupl 202 | repo 203 | 204 | 205 | 206 | 207 | Arne Seime 208 | arne.seime@entur.org 209 | Entur 210 | http://www.entur.org 211 | 212 | 213 | Dainius Figoras 214 | dainius.figoras@entur.org 215 | Entur 216 | http://www.entur.org 217 | 218 | 219 | 220 | scm:git:ssh://git@github.com/entur/mapstruct-spi-protobuf.git 221 | scm:git:ssh://git@github.com/entur/mapstruct-spi-protobuf.git 222 | https://github.com/entur/mapstruct-spi-protobuf/tree/master 223 | HEAD 224 | 225 | 226 | 227 | ossrh 228 | Sonatype release 229 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 230 | 231 | 232 | ossrh 233 | Sonatype snapshot 234 | https://oss.sonatype.org/content/repositories/snapshots 235 | 236 | 237 | 238 | 239 | entur-maven-central-local 240 | 241 | false 242 | 243 | 244 | 245 | entur-maven-central-local 246 | Entur maven central local 247 | https://entur2.jfrog.io/entur2/entur-maven-central-release-local 248 | 249 | 250 | 251 | 252 | release 253 | 254 | false 255 | 256 | 257 | 258 | 259 | org.apache.maven.plugins 260 | maven-gpg-plugin 261 | 3.2.7 262 | 263 | 264 | sign-artifacts 265 | 266 | sign 267 | 268 | verify 269 | 270 | ${env.SONATYPE_GPG_KEY_PASSWORD} 271 | 272 | --pinentry-mode 273 | loopback 274 | 275 | 276 | 277 | 278 | 279 | 280 | org.sonatype.plugins 281 | nexus-staging-maven-plugin 282 | 1.6.13 283 | true 284 | 285 | ossrh 286 | https://oss.sonatype.org/ 287 | true 288 | 289 | 290 | 291 | org.apache.maven.plugins 292 | maven-source-plugin 293 | ${maven-source-plugin.version} 294 | 295 | 296 | attach-sources 297 | 298 | jar 299 | 300 | 301 | 302 | 303 | 304 | org.apache.maven.plugins 305 | maven-javadoc-plugin 306 | ${maven-javadoc-plugin.version} 307 | 308 | ${maven.compiler.source} 309 | none 310 | 311 | 312 | 313 | attach-javadocs 314 | 315 | jar 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { "enabled": false } 2 | -------------------------------------------------------------------------------- /spi-impl/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | no.entur.mapstruct.spi 6 | spi-protobuf-parent 7 | 0.1-SNAPSHOT 8 | 9 | protobuf-spi-impl 10 | 0.1-SNAPSHOT 11 | 12 | 13 | org.mapstruct 14 | mapstruct-processor 15 | 16 | 17 | org.mapstruct 18 | mapstruct 19 | 20 | 21 | com.google.guava 22 | guava 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-source-plugin 30 | 31 | 32 | com.diffplug.spotless 33 | spotless-maven-plugin 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 39 | 40 | default-compile 41 | 42 | -proc:none 43 | 44 | no/entur/mapstruct/spi/protobuf/ProcessingEnvOptionsHolder.java 45 | 46 | 47 | 48 | 49 | compile-project 50 | 51 | compile 52 | 53 | compile 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /spi-impl/src/main/java/no/entur/mapstruct/spi/protobuf/ProcessingEnvOptionsHolder.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-spi-impl 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import java.util.Map; 27 | import java.util.Set; 28 | 29 | import javax.annotation.processing.AbstractProcessor; 30 | import javax.annotation.processing.ProcessingEnvironment; 31 | import javax.annotation.processing.RoundEnvironment; 32 | import javax.annotation.processing.SupportedAnnotationTypes; 33 | import javax.annotation.processing.SupportedOptions; 34 | import javax.lang.model.SourceVersion; 35 | import javax.lang.model.element.TypeElement; 36 | 37 | import com.google.common.collect.ImmutableMap; 38 | 39 | /** 40 | * This is not a true processor. It merely exists to pass the defined supported options to a global context that is accessible by the MapStruct classes which 41 | * would otherwise not have visibility to these. 42 | */ 43 | @SupportedAnnotationTypes({}) 44 | @SupportedOptions({ ProcessingEnvOptionsHolder.ENUM_POSTFIX_OVERRIDES }) 45 | public class ProcessingEnvOptionsHolder extends AbstractProcessor { 46 | 47 | static final String ENUM_POSTFIX_OVERRIDES = "mapstructSpi.enumPostfixOverrides"; 48 | 49 | private static Map OPTIONS; 50 | 51 | @Override 52 | public synchronized void init(ProcessingEnvironment processingEnv) { 53 | super.init(processingEnv); 54 | OPTIONS = ImmutableMap.copyOf(processingEnv.getOptions()); 55 | } 56 | 57 | @Override 58 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 59 | return false; 60 | } 61 | 62 | @Override 63 | public SourceVersion getSupportedSourceVersion() { 64 | return SourceVersion.latestSupported(); 65 | } 66 | 67 | static boolean containsKey(String key) { 68 | if (OPTIONS == null) { 69 | throw new IllegalStateException("ProcessingEnvOptionsHolder not initialized yet."); 70 | } 71 | return OPTIONS.containsKey(key); 72 | } 73 | 74 | static String getOption(String key) { 75 | if (OPTIONS == null) { 76 | throw new IllegalStateException("ProcessingEnvOptionsHolder not initialized yet."); 77 | } 78 | return OPTIONS.get(key); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /spi-impl/src/main/java/no/entur/mapstruct/spi/protobuf/ProtobufAccessorNamingStrategy.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-spi-impl 6 | * %% 7 | * Copyright (C) 2019 - 2020 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import java.util.Arrays; 27 | import java.util.HashSet; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.Set; 31 | 32 | import javax.lang.model.element.Element; 33 | import javax.lang.model.element.ElementKind; 34 | import javax.lang.model.element.ExecutableElement; 35 | import javax.lang.model.element.TypeElement; 36 | import javax.lang.model.type.DeclaredType; 37 | import javax.lang.model.type.TypeMirror; 38 | 39 | import org.mapstruct.ap.internal.util.Nouns; 40 | import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy; 41 | import org.mapstruct.ap.spi.MapStructProcessingEnvironment; 42 | import org.mapstruct.ap.spi.util.IntrospectorUtils; 43 | 44 | /** 45 | * @author Arne Seime 46 | */ 47 | public class ProtobufAccessorNamingStrategy extends DefaultAccessorNamingStrategy { 48 | public static final String PROTOBUF_STRING_LIST_TYPE = "com.google.protobuf.ProtocolStringList"; 49 | public static final String PROTOBUF_MESSAGE_OR_BUILDER = "com.google.protobuf.MessageLiteOrBuilder"; 50 | public static final String PROTOBUF_BUILDER = "com.google.protobuf.GeneratedMessageV3.Builder"; 51 | public static final String BUILDER_LIST_SUFFIX = "BuilderList"; 52 | 53 | protected static final Set INTERNAL_METHODS = new HashSet<>(Arrays.asList("newBuilder", "newBuilderForType", "parseFrom", "parseDelimitedFrom", 54 | "getDefaultInstance", "getDescriptor", "getDescriptorForType", "getDefaultInstanceForType", "clear", "clearField", "clearOneof", "mergeFrom", 55 | "setRepeatedField", "setUnknownFields", "getSerializedSize", "getAllFields", "getAllFieldsMutable", "getAllFieldsRaw", "getDescriptorForType", 56 | "getField", "getFieldRaw", "getOneofFieldDescriptor", "getParserForType", "getRepeatedField", "getRepeatedFieldCount", "getUnknownFields", 57 | "getInitializationErrorString", "getMemoizedSerializedSize", "getOneofFieldDescriptor", "getSerializedSize", "getMemoizedSerializedSize", 58 | "getSerializingExceptionMessage", "isInitialized", "mergeUnknownFields")); 59 | 60 | protected static final List INTERNAL_SPECIAL_METHOD_ENDINGS = Arrays.asList("Value", "Count", "Bytes", "Map", "ValueList"); 61 | 62 | protected static final List INTERNAL_SPECIAL_METHOD_BEGINNINGS = Arrays.asList("remove", "clear", "mutable", "merge", "putAll"); 63 | 64 | protected TypeMirror protobufMesageOrBuilderType; 65 | 66 | @Override 67 | public void init(MapStructProcessingEnvironment processingEnvironment) { 68 | super.init(processingEnvironment); 69 | 70 | TypeElement typeElement = elementUtils.getTypeElement(PROTOBUF_MESSAGE_OR_BUILDER); 71 | if (typeElement != null) { 72 | protobufMesageOrBuilderType = typeElement.asType(); 73 | } 74 | } 75 | 76 | private boolean isSpecialMethod(ExecutableElement method) { 77 | if (!isMethodFromProtobufGeneratedClass(method)) { 78 | return false; 79 | } 80 | String methodName = method.getSimpleName().toString(); 81 | 82 | for (String checkMethod : INTERNAL_SPECIAL_METHOD_ENDINGS) { 83 | if (methodName.endsWith(checkMethod)) { 84 | String propertyMethod = methodName.substring(0, methodName.length() - checkMethod.length()); 85 | boolean propertyMethodExists = method.getEnclosingElement() 86 | .getEnclosedElements() 87 | .stream() 88 | .anyMatch(e -> e.getSimpleName().toString().equals(propertyMethod)); 89 | if (propertyMethodExists) { 90 | return true; 91 | } 92 | } 93 | } 94 | 95 | for (String checkMethod : INTERNAL_SPECIAL_METHOD_BEGINNINGS) { 96 | if (methodName.startsWith(checkMethod)) { 97 | String propertyMethod = "get" + methodName.substring(checkMethod.length()); 98 | 99 | boolean propertyMethodExists = method.getEnclosingElement() 100 | .getEnclosedElements() 101 | .stream() 102 | .anyMatch(e -> (e).getSimpleName().toString().equals(propertyMethod)); 103 | if (propertyMethodExists) { 104 | return true; 105 | } 106 | } 107 | } 108 | 109 | if (isOneOfCaseSelector(method)) { 110 | return true; 111 | } 112 | 113 | return false; 114 | } 115 | 116 | @Override 117 | public boolean isGetterMethod(ExecutableElement method) { 118 | String methodName = method.getSimpleName().toString(); 119 | 120 | // if (methodName.endsWith("Builder")) { 121 | // return false; 122 | // } 123 | 124 | if (methodName.endsWith("OrBuilder")) { 125 | return false; 126 | } 127 | 128 | if (methodName.endsWith("OrBuilderList")) { 129 | return false; 130 | } 131 | 132 | if (methodName.endsWith(BUILDER_LIST_SUFFIX)) { 133 | return false; 134 | } 135 | 136 | if (INTERNAL_METHODS.contains(methodName)) { 137 | return false; 138 | } else { 139 | if (isSpecialMethod(method)) { 140 | return false; 141 | } 142 | if (isGetMutableMap(method)) { 143 | return true; 144 | } 145 | if (isGetUnmodifiableMap(method)) { 146 | return !isMethodFromProtobufBuilder(method); 147 | } 148 | return super.isGetterMethod(method); 149 | } 150 | 151 | } 152 | 153 | private boolean isOneOfCaseSelector(ExecutableElement method) { 154 | String methodName = method.getSimpleName().toString(); 155 | if (methodName.startsWith("getOneOf") && methodName.endsWith("Case")) { 156 | return true; 157 | } 158 | return false; 159 | } 160 | 161 | @Override 162 | public boolean isSetterMethod(ExecutableElement method) { 163 | String methodName = method.getSimpleName().toString(); 164 | 165 | if (INTERNAL_METHODS.contains(methodName)) { 166 | return false; 167 | } else { 168 | if (isSpecialMethod(method)) { 169 | return false; 170 | } 171 | return super.isSetterMethod(method); 172 | } 173 | } 174 | 175 | @Override 176 | protected boolean isFluentSetter(ExecutableElement method) { 177 | String methodName = method.getSimpleName().toString(); 178 | 179 | if (INTERNAL_METHODS.contains(methodName)) { 180 | return false; 181 | } else if (methodName.startsWith("get")) { 182 | // Protobuf fluent setters should always start with set, if it starts with get it is probably a getter 183 | // for a builder in a list field with recursive references (param being index). 184 | // For instance UserDTO.getUsersBuilder(idx) in provided test case 185 | return false; 186 | } else { 187 | if (isOneOfCaseSelector(method)) { 188 | return false; 189 | } 190 | 191 | return super.isFluentSetter(method); 192 | } 193 | } 194 | 195 | @Override 196 | public boolean isAdderMethod(ExecutableElement method) { 197 | String methodName = method.getSimpleName().toString(); 198 | 199 | if (INTERNAL_METHODS.contains(methodName)) { 200 | return false; 201 | } else { 202 | if (isOneOfCaseSelector(method)) { 203 | return false; 204 | } 205 | return super.isAdderMethod(method); 206 | } 207 | } 208 | 209 | @Override 210 | public boolean isPresenceCheckMethod(ExecutableElement method) { 211 | String methodName = method.getSimpleName().toString(); 212 | 213 | if (INTERNAL_METHODS.contains(methodName)) { 214 | return false; 215 | } else { 216 | if (isOneOfCaseSelector(method)) { 217 | return true; 218 | } 219 | return super.isPresenceCheckMethod(method); 220 | } 221 | } 222 | 223 | @Override 224 | public String getElementName(ExecutableElement adderMethod) { 225 | 226 | String methodName = super.getElementName(adderMethod); 227 | if (isMethodFromProtobufGeneratedClass(adderMethod)) { 228 | String singularizedMethodName = Nouns.singularize(methodName); 229 | methodName = singularizedMethodName; 230 | } 231 | 232 | return methodName; 233 | } 234 | 235 | @Override 236 | public String getPropertyName(ExecutableElement getterOrSetterMethod) { 237 | String methodName = getterOrSetterMethod.getSimpleName().toString(); 238 | 239 | boolean isGetMutableMap = isGetMutableMap(getterOrSetterMethod); 240 | boolean isGetUnmodifiableMap = isGetUnmodifiableMap(getterOrSetterMethod); 241 | 242 | if (isGetList(getterOrSetterMethod) || isSetList(getterOrSetterMethod) || isGetMutableMap) { 243 | Element receiver = getterOrSetterMethod.getEnclosingElement(); 244 | if (receiver != null && (receiver.getKind() == ElementKind.CLASS || receiver.getKind() == ElementKind.INTERFACE)) { 245 | TypeElement type = (TypeElement) receiver; 246 | if (isProtobufGeneratedMessage(type)) { 247 | if (isGetMutableMap) { 248 | // 'getMutable...' 249 | return IntrospectorUtils.decapitalize(methodName.substring(10)); 250 | } else if (isGetUnmodifiableMap) { 251 | // 'get...' 252 | return IntrospectorUtils.decapitalize(methodName.substring(3)); 253 | } else { 254 | // 'get...List' 255 | return IntrospectorUtils.decapitalize(methodName.substring(3, methodName.length() - 4)); 256 | } 257 | } 258 | } 259 | } 260 | return super.getPropertyName(getterOrSetterMethod); 261 | } 262 | 263 | private boolean isGetList(ExecutableElement element) { 264 | return element.getSimpleName().toString().startsWith("get") && isListType(element.getReturnType()); 265 | } 266 | 267 | private boolean isGetUnmodifiableMap(ExecutableElement element) { 268 | return element.getSimpleName().toString().startsWith("get") && !element.getSimpleName().toString().startsWith("getMutable") 269 | // skip 'get..'.Map pattern (this method are duplicated by 'get...') 270 | && !element.getSimpleName().toString().endsWith("Map") && isMapType(element.getReturnType()); 271 | } 272 | 273 | private boolean isGetMutableMap(ExecutableElement element) { 274 | return element.getSimpleName().toString().startsWith("getMutable") && isMapType(element.getReturnType()); 275 | } 276 | 277 | private boolean isSetList(ExecutableElement element) { 278 | if (element.getSimpleName().toString().startsWith("set") && element.getParameters().size() == 1) { 279 | TypeMirror param = element.getParameters().get(0).asType(); 280 | return isListType(param); 281 | } 282 | return false; 283 | } 284 | 285 | private boolean isListType(TypeMirror t) { 286 | return t.toString().startsWith(List.class.getCanonicalName()) || t.toString().startsWith(PROTOBUF_STRING_LIST_TYPE); 287 | } 288 | 289 | private boolean isMapType(TypeMirror t) { 290 | return t.toString().startsWith(Map.class.getCanonicalName()); 291 | } 292 | 293 | private boolean isProtobufGeneratedMessage(TypeElement type) { 294 | List interfaces = type.getInterfaces(); 295 | 296 | if (interfaces != null) { 297 | for (TypeMirror implementedInterface : interfaces) { 298 | if (implementedInterface.toString().startsWith(PROTOBUF_MESSAGE_OR_BUILDER)) { 299 | return true; 300 | } else if (implementedInterface instanceof DeclaredType) { 301 | DeclaredType declared = (DeclaredType) implementedInterface; 302 | Element supertypeElement = declared.asElement(); 303 | if (isProtobufGeneratedMessage((TypeElement) supertypeElement)) { 304 | return true; 305 | } 306 | } 307 | } 308 | } 309 | 310 | TypeMirror superType = type.getSuperclass(); 311 | if (superType instanceof DeclaredType) { 312 | DeclaredType declared = (DeclaredType) superType; 313 | Element supertypeElement = declared.asElement(); 314 | return isProtobufGeneratedMessage((TypeElement) supertypeElement); 315 | } 316 | return false; 317 | } 318 | 319 | private boolean isMethodFromProtobufGeneratedClass(ExecutableElement method) { 320 | Element receiver = method.getEnclosingElement(); 321 | return protobufMesageOrBuilderType != null && receiver != null && typeUtils.isAssignable(receiver.asType(), protobufMesageOrBuilderType); 322 | } 323 | 324 | private boolean isMethodFromProtobufBuilder(ExecutableElement method) { 325 | TypeElement cls = (TypeElement) method.getEnclosingElement(); 326 | return cls.getSuperclass().toString().startsWith(PROTOBUF_BUILDER); 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /spi-impl/src/main/java/no/entur/mapstruct/spi/protobuf/ProtobufEnumMappingStrategy.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-spi-impl 6 | * %% 7 | * Copyright (C) 2019 - 2020 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import java.util.Arrays; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.Optional; 31 | import java.util.stream.Collectors; 32 | 33 | import javax.lang.model.element.TypeElement; 34 | import javax.lang.model.type.TypeMirror; 35 | 36 | import org.mapstruct.MappingConstants; 37 | import org.mapstruct.ap.spi.DefaultEnumMappingStrategy; 38 | 39 | import com.google.common.base.CaseFormat; 40 | import com.google.common.collect.ImmutableMap; 41 | 42 | public class ProtobufEnumMappingStrategy extends DefaultEnumMappingStrategy { 43 | 44 | private static final String DEFAULT_ENUM_POSTFIX = "UNSPECIFIED"; 45 | private static final String UNPARSABLE_ENUM_CONSTANT_UNRECOGNIZED = "UNRECOGNIZED"; 46 | private static final String PROTOBUF_ENUM_INTERFACE = "com.google.protobuf.ProtocolMessageEnum"; 47 | private static final String PROTOBUF_LITE_ENUM_INTERFACE = "com.google.protobuf.Internal.EnumLite"; 48 | private static final HashMap KNOWN_ENUMS = new HashMap<>(); 49 | 50 | private Map enumPostfixOverrides = null; 51 | 52 | /** 53 | * The enum constant postfix used as default value in protobuf, ie for enum "Cake" the default constant should be CAKE_UNSPECIFIED = 0; This is the 54 | * recommended style according to Googles style guide https://developers.google.com/protocol-buffers/docs/style#enums. If you use some other pattern in your 55 | * protobuf files you can pass in "mapstructSpi.enumPostfixOverrides" as a compilerArg. 56 | */ 57 | private String getEnumPostfix(TypeElement enumType) { 58 | if (enumPostfixOverrides == null) { 59 | initEnumPostfixOverrides(); 60 | } 61 | 62 | String enumTypeName = enumType.getQualifiedName().toString(); 63 | Optional override = enumPostfixOverrides.keySet().stream().filter(enumTypeName::startsWith).map(enumPostfixOverrides::get).findAny(); 64 | 65 | return override.orElse(DEFAULT_ENUM_POSTFIX); 66 | } 67 | 68 | private void initEnumPostfixOverrides() { 69 | if (ProcessingEnvOptionsHolder.containsKey(ProcessingEnvOptionsHolder.ENUM_POSTFIX_OVERRIDES)) { 70 | String[] postfixOverrides = ProcessingEnvOptionsHolder.getOption(ProcessingEnvOptionsHolder.ENUM_POSTFIX_OVERRIDES).split(","); 71 | 72 | enumPostfixOverrides = Arrays.stream(postfixOverrides) 73 | .map(override -> override.split("=", 2)) 74 | .collect(Collectors.toMap(args -> args[0].trim(), args -> args[1].trim())); 75 | } else { 76 | enumPostfixOverrides = ImmutableMap.of(); 77 | } 78 | } 79 | 80 | // @Override 81 | public boolean isMapEnumConstantToNull(TypeElement enumType, String sourceEnumValue) { 82 | if (isProtobufEnum(enumType)) { 83 | if (UNPARSABLE_ENUM_CONSTANT_UNRECOGNIZED.equals(sourceEnumValue)) { 84 | return true; 85 | } 86 | 87 | String trimmedEnumValue = removeEnumNamePrefixFromConstant(enumType, sourceEnumValue); 88 | if (getEnumPostfix(enumType).equals(trimmedEnumValue)) { 89 | return true; 90 | } 91 | 92 | } 93 | return false; 94 | } 95 | 96 | @Override 97 | public String getDefaultNullEnumConstant(TypeElement enumType) { 98 | boolean isProtobufEnum = isProtobufEnum(enumType); 99 | 100 | if (isProtobufEnum) { 101 | return addEnumNamePrefixToConstant(enumType, getEnumPostfix(enumType)); 102 | } else { 103 | return null; 104 | } 105 | 106 | } 107 | 108 | @Override 109 | public String getEnumConstant(TypeElement enumType, String sourceEnumValue) { 110 | boolean isProtobufEnum = isProtobufEnum(enumType); 111 | 112 | if (isProtobufEnum) { 113 | if (isMapEnumConstantToNull(enumType, sourceEnumValue)) { 114 | return MappingConstants.NULL; 115 | } else if (sourceEnumValue == null) { 116 | return getDefaultNullEnumConstant(enumType); 117 | } 118 | 119 | return removeEnumNamePrefixFromConstant(enumType, sourceEnumValue); 120 | } else { 121 | return sourceEnumValue; 122 | 123 | } 124 | } 125 | 126 | private String addEnumNamePrefixToConstant(TypeElement enumType, String constant) { 127 | String enumName = enumType.getSimpleName().toString(); 128 | String prefix = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, enumName); 129 | 130 | String constructedValue = String.format("%s_%s", prefix, constant); 131 | return constructedValue; 132 | } 133 | 134 | private String removeEnumNamePrefixFromConstant(TypeElement enumType, String sourceEnumValue) { 135 | String enumName = enumType.getSimpleName().toString(); 136 | String prefix = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, enumName); 137 | 138 | String trimmedValue = sourceEnumValue.replace(prefix + "_", ""); 139 | return trimmedValue; 140 | } 141 | 142 | private boolean isProtobufEnum(TypeElement enumType) { 143 | Boolean isProtobufEnum = KNOWN_ENUMS.get(enumType); 144 | if (isProtobufEnum == null) { 145 | List interfaces = enumType.getInterfaces(); 146 | isProtobufEnum = Boolean.FALSE; 147 | for (TypeMirror implementedInterface : interfaces) { 148 | String implementedInterfaceName = implementedInterface.toString(); 149 | if (PROTOBUF_ENUM_INTERFACE.equals(implementedInterfaceName) || PROTOBUF_LITE_ENUM_INTERFACE.equals(implementedInterfaceName)) { 150 | isProtobufEnum = Boolean.TRUE; 151 | break; 152 | } 153 | } 154 | 155 | KNOWN_ENUMS.put(enumType, isProtobufEnum); 156 | } 157 | 158 | return isProtobufEnum; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /spi-impl/src/main/java/no/entur/mapstruct/spi/protobuf/ProtobufLiteBuilderProvider.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-spi-impl 6 | * %% 7 | * Copyright (C) 2019 - 2020 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import javax.lang.model.element.ExecutableElement; 27 | import javax.lang.model.element.TypeElement; 28 | import javax.lang.model.type.TypeMirror; 29 | import javax.lang.model.type.TypeVariable; 30 | 31 | import org.mapstruct.ap.spi.DefaultBuilderProvider; 32 | 33 | /** 34 | * Detect builder methods for protobuf lite generated classes. 35 | *

36 | * Protobuf lite builders are not detected as such by the DefaultBuilderProvider because the return type is not an explicit match for the desired TypeElement. 37 | * Relying instead on known name of builder method and superclass of return type. 38 | */ 39 | public class ProtobufLiteBuilderProvider extends DefaultBuilderProvider { 40 | 41 | public static final String PROTOBUF_LITE_GENERATED_MESSAGE = "com.google.protobuf.GeneratedMessageLite"; 42 | private static final String PROTOBUF_BUILD_METHOD_NAME = "build"; 43 | 44 | @Override 45 | protected boolean isBuildMethod(ExecutableElement buildMethod, TypeElement typeElement) { 46 | 47 | if (PROTOBUF_BUILD_METHOD_NAME.equals(buildMethod.getSimpleName().toString())) { 48 | if (buildMethod.getReturnType() instanceof TypeVariable) { 49 | TypeVariable tv = (TypeVariable) buildMethod.getReturnType(); 50 | 51 | TypeMirror upperBound = tv.getUpperBound() == null ? tv : tv.getUpperBound(); 52 | 53 | if (upperBound.toString().startsWith(PROTOBUF_LITE_GENERATED_MESSAGE)) { 54 | return true; 55 | } 56 | 57 | } 58 | } 59 | 60 | return super.isBuildMethod(buildMethod, typeElement); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /spi-impl/src/main/resources/META-INF/services/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | no.entur.mapstruct.spi.protobuf.ProcessingEnvOptionsHolder 2 | -------------------------------------------------------------------------------- /spi-impl/src/main/resources/META-INF/services/org.mapstruct.ap.spi.AccessorNamingStrategy: -------------------------------------------------------------------------------- 1 | no.entur.mapstruct.spi.protobuf.ProtobufAccessorNamingStrategy 2 | -------------------------------------------------------------------------------- /spi-impl/src/main/resources/META-INF/services/org.mapstruct.ap.spi.BuilderProvider: -------------------------------------------------------------------------------- 1 | no.entur.mapstruct.spi.protobuf.ProtobufLiteBuilderProvider 2 | -------------------------------------------------------------------------------- /spi-impl/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumMappingStrategy: -------------------------------------------------------------------------------- 1 | no.entur.mapstruct.spi.protobuf.ProtobufEnumMappingStrategy 2 | -------------------------------------------------------------------------------- /support-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | no.entur.mapstruct.spi 6 | spi-protobuf-parent 7 | 0.1-SNAPSHOT 8 | 9 | protobuf-support-core 10 | 0.1-SNAPSHOT 11 | 12 | 13 | org.mapstruct 14 | mapstruct 15 | 16 | 17 | org.slf4j 18 | slf4j-api 19 | 20 | 21 | com.google.api.grpc 22 | proto-google-common-protos 23 | provided 24 | 25 | 26 | org.junit.jupiter 27 | junit-jupiter-api 28 | test 29 | 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-source-plugin 36 | 37 | 38 | com.diffplug.spotless 39 | spotless-maven-plugin 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 45 | ${maven.compiler.source} 46 | ${maven.compiler.target} 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /support-core/src/main/java/no/entur/abt/mapstruct/common/ProtobufStandardMappings.java: -------------------------------------------------------------------------------- 1 | package no.entur.abt.mapstruct.common; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-support 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import com.google.protobuf.BoolValue; 27 | import com.google.protobuf.ByteString; 28 | import com.google.protobuf.BytesValue; 29 | import com.google.protobuf.DoubleValue; 30 | import com.google.protobuf.FloatValue; 31 | import com.google.protobuf.Int32Value; 32 | import com.google.protobuf.Int64Value; 33 | import com.google.protobuf.StringValue; 34 | import com.google.protobuf.Timestamp; 35 | import com.google.protobuf.UInt32Value; 36 | import com.google.protobuf.UInt64Value; 37 | 38 | import java.time.Instant; 39 | import java.time.LocalDate; 40 | import java.time.LocalDateTime; 41 | import java.time.LocalTime; 42 | import java.time.OffsetDateTime; 43 | import java.time.ZoneOffset; 44 | import java.util.GregorianCalendar; 45 | import java.util.TimeZone; 46 | 47 | public interface ProtobufStandardMappings { 48 | 49 | default ByteString mapByteString(byte[] array) { 50 | if (array == null) { 51 | return ByteString.EMPTY; 52 | } 53 | return ByteString.copyFrom(array); 54 | } 55 | 56 | default byte[] mapByteString(ByteString in) { 57 | if (in != null && !in.isEmpty()) { 58 | return in.toByteArray(); 59 | } 60 | 61 | return null; 62 | } 63 | 64 | default ByteString mapByteStringToString(String string) { 65 | return ByteString.copyFromUtf8(string); 66 | } 67 | 68 | default String mapStringToByteString(ByteString in) { 69 | if (in != null && !in.isEmpty()) { 70 | return in.toStringUtf8(); 71 | } 72 | 73 | return null; 74 | } 75 | 76 | default com.google.type.Date mapLocalDate(LocalDate t) { 77 | return com.google.type.Date.newBuilder().setYear(t.getYear()).setMonth(t.getMonthValue()).setDay(t.getDayOfMonth()).build(); 78 | } 79 | 80 | default LocalDate mapDate(com.google.type.Date t) { 81 | return LocalDate.of(t.getYear(), t.getMonth(), t.getDay()); 82 | } 83 | 84 | default com.google.type.TimeOfDay mapLocalTime(LocalTime t) { 85 | return com.google.type.TimeOfDay.newBuilder().setHours(t.getHour()).setMinutes(t.getMinute()).setSeconds(t.getSecond()).setNanos(t.getNano()).build(); 86 | } 87 | 88 | default LocalTime mapTimeOfDay(com.google.type.TimeOfDay t) { 89 | return LocalTime.of(t.getHours(), t.getMinutes(), t.getSeconds(), t.getNanos()); 90 | } 91 | 92 | default Timestamp map(LocalDateTime i) { 93 | if (i == null) { 94 | return null; 95 | } 96 | 97 | TimeZone systemDefault = TimeZone.getDefault(); 98 | 99 | int offset = systemDefault.getOffset(GregorianCalendar.AD, i.getYear(), i.getMonthValue() - 1, i.getDayOfMonth(), i.getDayOfWeek().getValue(), 100 | i.getNano() / 1000); 101 | 102 | return Timestamp.newBuilder().setSeconds(i.toEpochSecond(ZoneOffset.ofTotalSeconds(offset / 1000))).setNanos(i.getNano()).build(); 103 | } 104 | 105 | default Timestamp map(OffsetDateTime in) { 106 | return Timestamp.newBuilder().setSeconds(in.toEpochSecond()).setNanos(0).build(); 107 | } 108 | 109 | default float map(FloatValue f) { 110 | return f.getValue(); 111 | } 112 | 113 | default double map(DoubleValue f) { 114 | return f.getValue(); 115 | } 116 | 117 | default int map(Int32Value f) { 118 | return f.getValue(); 119 | } 120 | 121 | default long map(Int64Value f) { 122 | return f.getValue(); 123 | } 124 | 125 | default int map(UInt32Value f) { 126 | return f.getValue(); 127 | } 128 | 129 | default long map(UInt64Value f) { 130 | return f.getValue(); 131 | } 132 | 133 | default String map(StringValue f) { 134 | return f.getValue(); 135 | } 136 | 137 | default boolean map(BoolValue f) { 138 | return f.getValue(); 139 | } 140 | 141 | default ByteString map(BytesValue f) { 142 | return f.getValue(); 143 | } 144 | 145 | default Instant mapToInstant(Timestamp t) { 146 | if (t == null) { 147 | return null; 148 | } 149 | 150 | Timestamp sanitized = Timestamps.sanitize(t); 151 | 152 | if (sanitized != null) { 153 | return Instant.ofEpochSecond(sanitized.getSeconds(), sanitized.getNanos()); 154 | } else { 155 | return null; 156 | } 157 | } 158 | 159 | default Timestamp mapToTimestamp(Instant i) { 160 | if (i == null) { 161 | return null; 162 | } 163 | 164 | Timestamp t = Timestamp.newBuilder().setSeconds(i.getEpochSecond()).setNanos(i.getNano()).build(); 165 | return Timestamps.sanitize(t); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /support-core/src/main/java/no/entur/abt/mapstruct/common/Timestamps.java: -------------------------------------------------------------------------------- 1 | package no.entur.abt.mapstruct.common; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-support-lite 6 | * %% 7 | * Copyright (C) 2019 - 2022 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import com.google.protobuf.Timestamp; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | public class Timestamps { 31 | 32 | private static final Logger LOGGER = LoggerFactory.getLogger(Timestamps.class);; 33 | static final long TIMESTAMP_SECONDS_MIN = -62135596800L; 34 | static final long TIMESTAMP_SECONDS_MAX = 253402300799L; 35 | static final int NANOS_PER_SECOND = 1000000000; 36 | 37 | public static final Timestamp MAX_VALUE = Timestamp.newBuilder().setSeconds(TIMESTAMP_SECONDS_MAX).setNanos(NANOS_PER_SECOND - 1).build(); 38 | public static final Timestamp MIN_VALUE = Timestamp.newBuilder().setSeconds(TIMESTAMP_SECONDS_MIN).setNanos(0).build(); 39 | 40 | /** 41 | * Sanitize Timestamps outside legal range where possible. 42 | */ 43 | public static Timestamp sanitize(Timestamp t) { 44 | if (t.getSeconds() == 0 && t.getNanos() == 0) { 45 | return null; // Assuming null for epoch, cannot differentiate 46 | } 47 | if (t.getNanos() < 0 || t.getNanos() >= NANOS_PER_SECOND) { 48 | throw new IllegalArgumentException(String.format( 49 | "Timestamp is not valid. See proto definition for valid values. Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]. Nanos (%s) must be in range [0, +999,999,999].", 50 | t.getSeconds(), t.getNanos())); 51 | } 52 | if (t.getSeconds() > TIMESTAMP_SECONDS_MAX) { 53 | LOGGER.warn(String.format("Cannot map to timestamp greater than allowed MAX: (%s). Using MAX_TIMESTAMP instead: (%s)", t, Timestamps.MAX_VALUE)); 54 | return MAX_VALUE; 55 | } 56 | if (t.getSeconds() < TIMESTAMP_SECONDS_MIN) { 57 | LOGGER.warn(String.format("Cannot map to timestamp less than allowed MIN: (%s). Using MIN_TIMESTAMP instead: (%s)", t, Timestamps.MIN_VALUE)); 58 | return MIN_VALUE; 59 | } 60 | 61 | return t; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /support-core/src/test/java/no/entur/abt/mapstruct/common/ProtobufStandardMappingsTest.java: -------------------------------------------------------------------------------- 1 | package no.entur.abt.mapstruct.common; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-support 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | public class ProtobufStandardMappingsTest { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /support-lite/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | no.entur.mapstruct.spi 6 | spi-protobuf-parent 7 | 0.1-SNAPSHOT 8 | 9 | protobuf-support-lite 10 | 0.1-SNAPSHOT 11 | 12 | 13 | no.entur.mapstruct.spi 14 | protobuf-support-core 15 | 16 | 17 | org.mapstruct 18 | mapstruct 19 | 20 | 21 | com.google.api.grpc 22 | proto-google-common-protos 23 | provided 24 | 25 | 26 | org.junit.jupiter 27 | junit-jupiter-api 28 | test 29 | 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-source-plugin 36 | 37 | 38 | com.diffplug.spotless 39 | spotless-maven-plugin 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 45 | ${maven.compiler.source} 46 | ${maven.compiler.target} 47 | 48 | 49 | no.entur.mapstruct.spi 50 | protobuf-spi-impl 51 | ${project.parent.version} 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /support-lite/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java: -------------------------------------------------------------------------------- 1 | package no.entur.abt.mapstruct; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-support 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import java.time.Duration; 27 | import java.time.Instant; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | import org.mapstruct.Mapper; 31 | import org.mapstruct.factory.Mappers; 32 | 33 | import com.google.protobuf.Timestamp; 34 | 35 | /*** 36 | * 37 | * Note: This mapper must be kept in sync with its corresponding 'standard' equivalent 38 | * 39 | */ 40 | 41 | @Mapper 42 | public interface ProtobufStandardMappings extends no.entur.abt.mapstruct.common.ProtobufStandardMappings { 43 | 44 | ProtobufStandardMappings INSTANCE = Mappers.getMapper(ProtobufStandardMappings.class); 45 | 46 | default Long toEpochMilliseconds(Timestamp instance) { 47 | Instant instant = mapToInstant(instance); 48 | return instant == null ? null : instant.toEpochMilli(); 49 | } 50 | 51 | default Timestamp fromEpochMilliseconds(Long instance) { 52 | if (instance == null) { 53 | return null; 54 | } 55 | Instant instant = Instant.ofEpochMilli(instance); 56 | return mapToTimestamp(instant); 57 | } 58 | 59 | default Duration mapDuration(com.google.protobuf.Duration t) { 60 | return Duration.ofSeconds(t.getSeconds(), t.getNanos()); 61 | } 62 | 63 | default com.google.protobuf.Duration mapDuration(Duration t) { 64 | long seconds = t.getSeconds(); 65 | int nanos = t.getNano(); 66 | 67 | // Protobuf requires same sign for seconds & nanos parts. Java time treats nano part as relative adjustment. Adjust to proto expectations 68 | if (seconds < 0 && nanos > 0) { 69 | seconds = seconds + 1; 70 | nanos = (int) (nanos - TimeUnit.SECONDS.toNanos(1)); 71 | } 72 | 73 | return com.google.protobuf.Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /support-lite/src/test/java/no/entur/abt/mapstruct/Durations.java: -------------------------------------------------------------------------------- 1 | package no.entur.abt.mapstruct; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-support-lite 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | public class Durations { 27 | 28 | static final long NANOS_PER_SECOND = 1000000000; 29 | 30 | static final long DURATION_SECONDS_MIN = -315576000000L; 31 | static final long DURATION_SECONDS_MAX = 315576000000L; 32 | 33 | public static com.google.protobuf.Duration checkValid(com.google.protobuf.Duration duration) { 34 | long seconds = duration.getSeconds(); 35 | int nanos = duration.getNanos(); 36 | if (!isValid(seconds, nanos)) { 37 | throw new IllegalArgumentException(String.format( 38 | "Duration is not valid. See proto definition for valid values. " + "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]. " 39 | + "Nanos (%s) must be in range [-999,999,999, +999,999,999]. " + "Nanos must have the same sign as seconds", 40 | seconds, nanos)); 41 | } 42 | return duration; 43 | } 44 | 45 | public static boolean isValid(long seconds, int nanos) { 46 | if (seconds < DURATION_SECONDS_MIN || seconds > DURATION_SECONDS_MAX) { 47 | return false; 48 | } 49 | if (nanos < -999999999L || nanos >= NANOS_PER_SECOND) { 50 | return false; 51 | } 52 | if (seconds < 0 || nanos < 0) { 53 | if (seconds > 0 || nanos > 0) { 54 | return false; 55 | } 56 | } 57 | return true; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /support-lite/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java: -------------------------------------------------------------------------------- 1 | package no.entur.abt.mapstruct; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-support 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import static org.junit.jupiter.api.Assertions.assertEquals; 27 | import static org.junit.jupiter.api.Assertions.assertNull; 28 | 29 | import java.time.Duration; 30 | import java.time.Instant; 31 | import java.time.LocalDateTime; 32 | import java.time.ZoneId; 33 | import java.time.temporal.ChronoUnit; 34 | import java.util.concurrent.TimeUnit; 35 | 36 | import org.junit.jupiter.api.Test; 37 | 38 | import com.google.protobuf.Timestamp; 39 | 40 | import no.entur.abt.mapstruct.common.Timestamps; 41 | 42 | public class ProtobufStandardMappingsTest { 43 | 44 | ProtobufStandardMappings MAPPER = ProtobufStandardMappings.INSTANCE; 45 | 46 | @Test 47 | public void testMapLocalDateToTimestampSummertime() { 48 | LocalDateTime l = LocalDateTime.of(2000, 6, 1, 12, 0); 49 | 50 | Timestamp timestamp = MAPPER.map(l); 51 | Instant instant = MAPPER.mapToInstant(timestamp); 52 | 53 | LocalDateTime back = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 54 | 55 | assertEquals(l, back); 56 | } 57 | 58 | @Test 59 | public void testMapLocalDateToTimestampWintertime() { 60 | LocalDateTime l = LocalDateTime.of(2000, 2, 1, 12, 0); 61 | 62 | Timestamp timestamp = MAPPER.map(l); 63 | Instant instant = MAPPER.mapToInstant(timestamp); 64 | 65 | LocalDateTime back = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 66 | 67 | assertEquals(l, back); 68 | } 69 | 70 | @Test 71 | public void mapToInstant_whenSecondsAndNanosIs0_thenMapToNull() { 72 | assertNull(MAPPER.mapToInstant(Timestamp.newBuilder().build())); 73 | } 74 | 75 | @Test 76 | public void mapToInstant_whenNanosIsSet_thenMapToInstant() { 77 | assertEquals(3000, MAPPER.mapToInstant(Timestamp.newBuilder().setNanos(3000).build()).getNano()); 78 | } 79 | 80 | @Test 81 | public void mapToInstant_whenValueIsTooLargeForRangeForTimestamp_thenMapFromMaxValidTimestamp() { 82 | assertEquals(MAPPER.mapToInstant(Timestamps.MAX_VALUE), MAPPER.mapToInstant(Timestamp.newBuilder().setSeconds(Long.MAX_VALUE).build())); 83 | } 84 | 85 | @Test 86 | public void mapToInstant_whenValueIsTooSmallForRangeForTimestamp_thenMapFromMinValidTimestamp() { 87 | assertEquals(MAPPER.mapToInstant(Timestamps.MIN_VALUE), MAPPER.mapToInstant(Timestamp.newBuilder().setSeconds(-Long.MAX_VALUE).build())); 88 | } 89 | 90 | @Test 91 | public void mapInstantToTimestamp_whenValueIsTooLargeForRangeForTimestamp_thenMapToMaxValidTimestamp() { 92 | assertEquals(Timestamps.MAX_VALUE, MAPPER.mapToTimestamp(Instant.now().plus(Integer.MAX_VALUE, ChronoUnit.DAYS))); 93 | } 94 | 95 | @Test 96 | public void mapInstantToTimestamp_whenValueIsTooSmallForRangeForTimestamp_thenMapToMinValidTimestamp() { 97 | assertEquals(Timestamps.MIN_VALUE, MAPPER.mapToTimestamp(Instant.now().minus(Integer.MAX_VALUE, ChronoUnit.DAYS))); 98 | } 99 | 100 | public void mapPositiveDuration() { 101 | Duration duration = Duration.of(3, ChronoUnit.NANOS); 102 | 103 | com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); 104 | Durations.checkValid(pbDuration); 105 | assertEquals(duration, MAPPER.mapDuration(pbDuration)); 106 | } 107 | 108 | @Test 109 | public void mapNegativeDurationToProto_whenSecondsAreNegativeAndNanoPositive() { 110 | Duration duration = Duration.ofSeconds(-3, 2); 111 | 112 | com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); 113 | Durations.checkValid(pbDuration); 114 | assertEquals(duration, MAPPER.mapDuration(pbDuration)); 115 | } 116 | 117 | @Test 118 | public void mapNegativeDurationToProto_whenSecondsArePositiveAndNanoNegative() { 119 | // Duration.ofSeconds accepts negative values. Will still be stored as positive values in Duration 120 | Duration duration = Duration.ofSeconds(3, -(TimeUnit.SECONDS.toNanos(1) - 2)); 121 | 122 | com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); 123 | Durations.checkValid(pbDuration); 124 | assertEquals(duration, MAPPER.mapDuration(pbDuration)); 125 | } 126 | 127 | @Test 128 | public void mapNegativeDuration_fromProto() { 129 | com.google.protobuf.Duration pbDuration = com.google.protobuf.Duration.newBuilder().setSeconds(-10).setNanos(-5).build(); 130 | 131 | Duration duration = MAPPER.mapDuration(pbDuration); 132 | Durations.checkValid(pbDuration); 133 | assertEquals(pbDuration, MAPPER.mapDuration(duration)); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /support-standard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | no.entur.mapstruct.spi 6 | spi-protobuf-parent 7 | 0.1-SNAPSHOT 8 | 9 | protobuf-support-standard 10 | 0.1-SNAPSHOT 11 | 12 | 13 | no.entur.mapstruct.spi 14 | protobuf-support-core 15 | 16 | 17 | org.mapstruct 18 | mapstruct 19 | 20 | 21 | com.google.protobuf 22 | protobuf-java-util 23 | ${proto.version} 24 | 25 | 26 | com.google.api.grpc 27 | proto-google-common-protos 28 | provided 29 | 30 | 31 | org.junit.jupiter 32 | junit-jupiter-api 33 | test 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-source-plugin 41 | 42 | 43 | com.diffplug.spotless 44 | spotless-maven-plugin 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-compiler-plugin 49 | 50 | ${maven.compiler.source} 51 | ${maven.compiler.target} 52 | 53 | 54 | no.entur.mapstruct.spi 55 | protobuf-spi-impl 56 | ${project.parent.version} 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /support-standard/src/main/java/no/entur/abt/mapstruct/ProtobufStandardMappings.java: -------------------------------------------------------------------------------- 1 | package no.entur.abt.mapstruct; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-support 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import java.time.Duration; 27 | 28 | import org.mapstruct.Mapper; 29 | import org.mapstruct.factory.Mappers; 30 | 31 | import com.google.protobuf.Timestamp; 32 | import com.google.protobuf.util.Durations; 33 | import com.google.protobuf.util.Timestamps; 34 | 35 | /*** 36 | * 37 | * Note: This mapper must be kept in sync with its corresponding 'lite' equivalent 38 | * 39 | */ 40 | 41 | @Mapper 42 | public interface ProtobufStandardMappings extends no.entur.abt.mapstruct.common.ProtobufStandardMappings { 43 | 44 | ProtobufStandardMappings INSTANCE = Mappers.getMapper(ProtobufStandardMappings.class); 45 | 46 | default Long toEpochMilliseconds(Timestamp instance) { 47 | if (instance != null) { 48 | return Timestamps.toMillis(instance); 49 | } else { 50 | return null; 51 | } 52 | } 53 | 54 | default Timestamp fromEpochMilliseconds(long millis) { 55 | return Timestamps.fromMillis(millis); 56 | } 57 | 58 | default Duration mapDuration(com.google.protobuf.Duration t) { 59 | if (t != null) { 60 | return Duration.ofSeconds(t.getSeconds(), t.getNanos()); 61 | } else { 62 | return null; 63 | } 64 | } 65 | 66 | default com.google.protobuf.Duration mapDuration(Duration t) { 67 | if (t != null) { 68 | return Durations.fromNanos(t.toNanos()); 69 | } else { 70 | return null; 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /support-standard/src/test/java/no/entur/abt/mapstruct/ProtobufStandardMappingsTest.java: -------------------------------------------------------------------------------- 1 | package no.entur.abt.mapstruct; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-support 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import com.google.protobuf.Timestamp; 27 | import com.google.protobuf.util.Durations; 28 | import no.entur.abt.mapstruct.common.Timestamps; 29 | import org.junit.jupiter.api.Test; 30 | 31 | import java.time.Duration; 32 | import java.time.Instant; 33 | import java.time.LocalDateTime; 34 | import java.time.ZoneId; 35 | import java.time.temporal.ChronoUnit; 36 | import java.util.concurrent.TimeUnit; 37 | 38 | import static org.junit.jupiter.api.Assertions.assertEquals; 39 | import static org.junit.jupiter.api.Assertions.assertNull; 40 | 41 | public class ProtobufStandardMappingsTest { 42 | 43 | no.entur.abt.mapstruct.ProtobufStandardMappings MAPPER = no.entur.abt.mapstruct.ProtobufStandardMappings.INSTANCE; 44 | 45 | @Test 46 | public void testMapLocalDateToTimestampSummertime() { 47 | LocalDateTime l = LocalDateTime.of(2000, 6, 1, 12, 0); 48 | 49 | Timestamp timestamp = MAPPER.map(l); 50 | Instant instant = MAPPER.mapToInstant(timestamp); 51 | 52 | LocalDateTime back = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 53 | 54 | assertEquals(l, back); 55 | } 56 | 57 | @Test 58 | public void testMapLocalDateToTimestampWintertime() { 59 | LocalDateTime l = LocalDateTime.of(2000, 2, 1, 12, 0); 60 | 61 | Timestamp timestamp = MAPPER.map(l); 62 | Instant instant = MAPPER.mapToInstant(timestamp); 63 | 64 | LocalDateTime back = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 65 | 66 | assertEquals(l, back); 67 | } 68 | 69 | @Test 70 | public void mapToInstant_whenSecondsAndNanosIs0_thenMapToNull() { 71 | assertNull(MAPPER.mapToInstant(Timestamp.newBuilder().build())); 72 | } 73 | 74 | @Test 75 | public void mapToInstant_whenSecondsAndNanosIsNull_thenMapToNull() { 76 | assertNull(MAPPER.mapToInstant(null)); 77 | } 78 | 79 | @Test 80 | public void mapToInstant_whenNanosIsSet_thenMapToInstant() { 81 | assertEquals(3000, MAPPER.mapToInstant(Timestamp.newBuilder().setNanos(3000).build()).getNano()); 82 | } 83 | 84 | @Test 85 | public void mapToInstant_whenValueIsTooLargeForRangeForTimestamp_thenMapFromMaxValidTimestamp() { 86 | assertEquals(MAPPER.mapToInstant(Timestamps.MAX_VALUE), MAPPER.mapToInstant(Timestamp.newBuilder().setSeconds(Long.MAX_VALUE).build())); 87 | } 88 | 89 | @Test 90 | public void mapToInstant_whenValueIsTooSmallForRangeForTimestamp_thenMapFromMinValidTimestamp() { 91 | assertEquals(MAPPER.mapToInstant(Timestamps.MIN_VALUE), MAPPER.mapToInstant(Timestamp.newBuilder().setSeconds(-Long.MAX_VALUE).build())); 92 | } 93 | 94 | @Test 95 | public void mapInstantToTimestamp_whenValueIsTooLargeForRangeForTimestamp_thenMapToMaxValidTimestamp() { 96 | assertEquals(Timestamps.MAX_VALUE, MAPPER.mapToTimestamp(Instant.now().plus(Integer.MAX_VALUE, ChronoUnit.DAYS))); 97 | } 98 | 99 | @Test 100 | public void mapInstantToTimestamp_whenValueIsTooSmallForRangeForTimestamp_thenMapToMinValidTimestamp() { 101 | assertEquals(Timestamps.MIN_VALUE, MAPPER.mapToTimestamp(Instant.now().minus(Integer.MAX_VALUE, ChronoUnit.DAYS))); 102 | } 103 | 104 | @Test 105 | public void mapPositiveDuration() { 106 | Duration duration = Duration.of(3, ChronoUnit.NANOS); 107 | 108 | com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); 109 | Durations.checkValid(pbDuration); 110 | assertEquals(duration, MAPPER.mapDuration(pbDuration)); 111 | } 112 | 113 | @Test 114 | public void mapNegativeDurationToProto_whenSecondsAreNegativeAndNanoPositive() { 115 | Duration duration = Duration.ofSeconds(-3, 2); 116 | 117 | com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); 118 | Durations.checkValid(pbDuration); 119 | assertEquals(duration, MAPPER.mapDuration(pbDuration)); 120 | } 121 | 122 | @Test 123 | public void mapNegativeDurationToProto_whenSecondsArePositiveAndNanoNegative() { 124 | // Duration.ofSeconds accepts negative values. Will still be stored as positive values in Duration 125 | Duration duration = Duration.ofSeconds(3, -(TimeUnit.SECONDS.toNanos(1) - 2)); 126 | 127 | com.google.protobuf.Duration pbDuration = MAPPER.mapDuration(duration); 128 | Durations.checkValid(pbDuration); 129 | assertEquals(duration, MAPPER.mapDuration(pbDuration)); 130 | } 131 | 132 | @Test 133 | public void mapNegativeDuration_fromProto() { 134 | com.google.protobuf.Duration pbDuration = com.google.protobuf.Duration.newBuilder().setSeconds(-10).setNanos(-5).build(); 135 | 136 | Duration duration = MAPPER.mapDuration(pbDuration); 137 | Durations.checkValid(pbDuration); 138 | assertEquals(pbDuration, MAPPER.mapDuration(duration)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /usage/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | no.entur.mapstruct.spi 6 | spi-protobuf-parent 7 | 0.1-SNAPSHOT 8 | 9 | protobuf-usage 10 | 0.1-SNAPSHOT 11 | 12 | 13 | org.mapstruct 14 | mapstruct 15 | 16 | 17 | com.google.protobuf 18 | protobuf-java 19 | 20 | 21 | org.junit.jupiter 22 | junit-jupiter-engine 23 | test 24 | 25 | 26 | 27 | 28 | 29 | dev.cookiecode 30 | protobuf-maven-plugin 31 | ${protobuf.plugin.version} 32 | 33 | 34 | 35 | compile 36 | 37 | generate-sources 38 | 39 | com.google.protobuf:protoc:${proto.version}:exe:${os.detected.classifier} 40 | 41 | 42 | 43 | 44 | 45 | maven-compiler-plugin 46 | 47 | 48 | 49 | 50 | no.entur.mapstruct.spi 51 | protobuf-spi-impl 52 | ${project.version} 53 | 60 | 61 | 62 | 63 | -AmapstructSpi.enumPostfixOverrides=no.entur.mapstruct.example.EnumPostfixOverrideProtos=INVALID 64 | 65 | 66 | 67 | 68 | com.diffplug.spotless 69 | spotless-maven-plugin 70 | 71 | 72 | 73 | 74 | kr.motd.maven 75 | os-maven-plugin 76 | ${os.mavenplugin.version} 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/example/EnumPostfixOverrideMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) 3 | * and/or other contributors as indicated by the @authors tag. See the 4 | * copyright.txt file in the distribution for a full listing of all 5 | * contributors. 6 | *

7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | *

11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | *

13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | package no.entur.mapstruct.example; 20 | 21 | /*- 22 | * #%L 23 | * protobuf-usage 24 | * %% 25 | * Copyright (C) 2019 - 2020 Entur 26 | * %% 27 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 28 | * approved by the European Commission - subsequent versions of the 29 | * EUPL (the "Licence"); 30 | * 31 | * You may not use this work except in compliance with the Licence. 32 | * You may obtain a copy of the Licence at: 33 | * 34 | * http://ec.europa.eu/idabc/eupl5 35 | * 36 | * Unless required by applicable law or agreed to in writing, software 37 | * distributed under the Licence is distributed on an "AS IS" basis, 38 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 39 | * See the Licence for the specific language governing permissions and 40 | * limitations under the Licence. 41 | * #L% 42 | */ 43 | 44 | import org.mapstruct.CollectionMappingStrategy; 45 | import org.mapstruct.Mapper; 46 | import org.mapstruct.NullValueCheckStrategy; 47 | import org.mapstruct.ReportingPolicy; 48 | import org.mapstruct.factory.Mappers; 49 | 50 | import no.entur.mapstruct.example.EnumPostfixOverrideProtos.EnumPostfixOverrideValuesDTO; 51 | import no.entur.mapstruct.spi.protobuf.EnumPostfixOverrideValues; 52 | 53 | @Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedSourcePolicy = ReportingPolicy.ERROR, unmappedTargetPolicy = ReportingPolicy.ERROR) 54 | public interface EnumPostfixOverrideMapper { 55 | 56 | EnumPostfixOverrideMapper INSTANCE = Mappers.getMapper(EnumPostfixOverrideMapper.class); 57 | 58 | EnumPostfixOverrideValuesDTO map(EnumPostfixOverrideValues value); 59 | 60 | EnumPostfixOverrideValues map(EnumPostfixOverrideValuesDTO dto); 61 | 62 | } 63 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/example/UserMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) 3 | * and/or other contributors as indicated by the @authors tag. See the 4 | * copyright.txt file in the distribution for a full listing of all 5 | * contributors. 6 | *

7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | *

11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | *

13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | package no.entur.mapstruct.example; 20 | 21 | /*- 22 | * #%L 23 | * protobuf-usage 24 | * %% 25 | * Copyright (C) 2019 - 2020 Entur 26 | * %% 27 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 28 | * approved by the European Commission - subsequent versions of the 29 | * EUPL (the "Licence"); 30 | * 31 | * You may not use this work except in compliance with the Licence. 32 | * You may obtain a copy of the Licence at: 33 | * 34 | * http://ec.europa.eu/idabc/eupl5 35 | * 36 | * Unless required by applicable law or agreed to in writing, software 37 | * distributed under the Licence is distributed on an "AS IS" basis, 38 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 39 | * See the Licence for the specific language governing permissions and 40 | * limitations under the Licence. 41 | * #L% 42 | */ 43 | 44 | import org.mapstruct.CollectionMappingStrategy; 45 | import org.mapstruct.Mapper; 46 | import org.mapstruct.NullValueCheckStrategy; 47 | import org.mapstruct.ReportingPolicy; 48 | import org.mapstruct.factory.Mappers; 49 | 50 | import no.entur.mapstruct.example.UserProtos.DepartmentDTO; 51 | import no.entur.mapstruct.example.UserProtos.PermissionDTO; 52 | import no.entur.mapstruct.example.UserProtos.UserDTO; 53 | import no.entur.mapstruct.spi.protobuf.Department; 54 | import no.entur.mapstruct.spi.protobuf.MultiNumber; 55 | import no.entur.mapstruct.spi.protobuf.Permission; 56 | import no.entur.mapstruct.spi.protobuf.Status; 57 | import no.entur.mapstruct.spi.protobuf.User; 58 | 59 | @Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedSourcePolicy = ReportingPolicy.ERROR, unmappedTargetPolicy = ReportingPolicy.ERROR) 60 | public interface UserMapper { 61 | 62 | UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); 63 | 64 | default String mapString(String in) { 65 | if ((null == in) || in.isEmpty()) { 66 | return null; 67 | } 68 | return in; 69 | } 70 | 71 | default Double mapDouble(Double in) { 72 | return in; 73 | } 74 | 75 | default MultiNumber map(UserProtos.MultiNumberDTO number) { 76 | return new MultiNumber(); 77 | } 78 | 79 | default UserProtos.MultiNumberDTO map(MultiNumber number) { 80 | return UserProtos.MultiNumberDTO.newBuilder().build(); 81 | } 82 | 83 | UserDTO map(User user); 84 | 85 | User map(UserDTO userDTO); 86 | 87 | Permission map(PermissionDTO permissionDTO); 88 | 89 | PermissionDTO map(Permission perm); 90 | 91 | Status map(UserProtos.EnumStatus permissionDTO); 92 | 93 | UserProtos.EnumStatus map(Status perm); 94 | 95 | Department map(DepartmentDTO departmentDTO); 96 | 97 | DepartmentDTO map(Department department); 98 | } 99 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/spi/protobuf/Department.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-usage 6 | * %% 7 | * Copyright (C) 2018 - 2020 Entur AS and original authors 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | /** 27 | * @author Thomas Kratz 28 | */ 29 | public class Department { 30 | private String name; 31 | 32 | public Department() { 33 | } 34 | 35 | public Department(String name) { 36 | this.name = name; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public void setName(String name) { 44 | this.name = name; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/spi/protobuf/EnumPostfixOverrideValues.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-usage 6 | * %% 7 | * Copyright (C) 2019 - 2021 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | public enum EnumPostfixOverrideValues { 27 | ONE, 28 | TWO 29 | } 30 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/spi/protobuf/House.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-usage 6 | * %% 7 | * Copyright (C) 2019 - 2020 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | public class House { 27 | private String name; 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/spi/protobuf/MultiNumber.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-usage 6 | * %% 7 | * Copyright (C) 2019 - 2020 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | public class MultiNumber { 27 | Number number; 28 | 29 | public Number getNumber() { 30 | return number; 31 | } 32 | 33 | public void setNumber(Number number) { 34 | this.number = number; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/spi/protobuf/Permission.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-usage 6 | * %% 7 | * Copyright (C) 2019 - 2020 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | /** 27 | * @author Thomas Kratz 28 | */ 29 | public enum Permission { 30 | 31 | ADMIN, 32 | USER, 33 | NONE 34 | } 35 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/spi/protobuf/Status.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-usage 6 | * %% 7 | * Copyright (C) 2019 - 2020 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | public enum Status { 27 | STARTED, 28 | STOPPED 29 | } 30 | -------------------------------------------------------------------------------- /usage/src/main/java/no/entur/mapstruct/spi/protobuf/User.java: -------------------------------------------------------------------------------- 1 | package no.entur.mapstruct.spi.protobuf; 2 | 3 | /*- 4 | * #%L 5 | * protobuf-usage 6 | * %% 7 | * Copyright (C) 2019 - 2020 Entur 8 | * %% 9 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 10 | * approved by the European Commission - subsequent versions of the 11 | * EUPL (the "Licence"); 12 | * 13 | * You may not use this work except in compliance with the Licence. 14 | * You may obtain a copy of the Licence at: 15 | * 16 | * http://ec.europa.eu/idabc/eupl5 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the Licence is distributed on an "AS IS" basis, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the Licence for the specific language governing permissions and 22 | * limitations under the Licence. 23 | * #L% 24 | */ 25 | 26 | import java.util.ArrayList; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | import com.google.protobuf.ByteString; 32 | 33 | /** 34 | * @author Thomas Kratz 35 | */ 36 | public class User { 37 | 38 | double v1; 39 | float v2; 40 | int v3; 41 | long v4; 42 | int v5; 43 | long v6; 44 | int v7; 45 | long v8; 46 | int v9; 47 | 48 | // public List getPermissions() { 49 | // return permissions; 50 | // } 51 | // 52 | // public void setPermissions(List permissions) { 53 | // this.permissions = permissions; 54 | // } 55 | long v10; 56 | int v11; 57 | long v12; 58 | boolean v13; 59 | String v14; 60 | ByteString v15; 61 | Status v16; 62 | User user; 63 | List rv1; 64 | List rv2; 65 | List rv3; 66 | List rv4; 67 | List rv5; 68 | List rv6; 69 | List rv7; 70 | List rv8; 71 | List rv9; 72 | List rv10; 73 | List rv11; 74 | List rv12; 75 | List rv13; 76 | List rv14; 77 | List rv15; 78 | List rv16; 79 | MultiNumber multiNumber; 80 | List repMultiNumbers; 81 | private String id; 82 | private String email; 83 | // private List permissions = new ArrayList<>(); 84 | private List mainDepartments = new ArrayList<>(); 85 | private List departments = new ArrayList<>(); 86 | private String nonRepeatableFieldWithSuffixList; 87 | private List users; 88 | 89 | private Department fireDepartment; 90 | private Department policeDepartment; 91 | 92 | private Map stringMap = new HashMap<>(); 93 | 94 | public Map getStringMap() { 95 | return stringMap; 96 | } 97 | 98 | public Map getEntityMap() { 99 | return entityMap; 100 | } 101 | 102 | private Map entityMap = new HashMap<>(); 103 | 104 | public Department getFireDepartment() { 105 | return fireDepartment; 106 | } 107 | 108 | public void setFireDepartment(Department fireDepartment) { 109 | this.fireDepartment = fireDepartment; 110 | } 111 | 112 | public Department getPoliceDepartment() { 113 | return policeDepartment; 114 | } 115 | 116 | public void setPoliceDepartment(Department policeDepartment) { 117 | this.policeDepartment = policeDepartment; 118 | } 119 | 120 | public String getId() { 121 | return id; 122 | } 123 | 124 | public void setId(String id) { 125 | this.id = id; 126 | } 127 | 128 | public String getEmail() { 129 | return email; 130 | } 131 | 132 | public void setEmail(String email) { 133 | this.email = email; 134 | } 135 | 136 | public List getDepartments() { 137 | return departments; 138 | } 139 | 140 | public void setDepartments(List departments) { 141 | this.departments = departments; 142 | } 143 | 144 | public List getMainDepartments() { 145 | return mainDepartments; 146 | } 147 | 148 | public void setMainDepartments(List mainDepartments) { 149 | this.mainDepartments = mainDepartments; 150 | } 151 | 152 | public double getV1() { 153 | return v1; 154 | } 155 | 156 | public void setV1(double v1) { 157 | this.v1 = v1; 158 | } 159 | 160 | public float getV2() { 161 | return v2; 162 | } 163 | 164 | public void setV2(float v2) { 165 | this.v2 = v2; 166 | } 167 | 168 | public int getV3() { 169 | return v3; 170 | } 171 | 172 | public void setV3(int v3) { 173 | this.v3 = v3; 174 | } 175 | 176 | public long getV4() { 177 | return v4; 178 | } 179 | 180 | public void setV4(long v4) { 181 | this.v4 = v4; 182 | } 183 | 184 | public int getV5() { 185 | return v5; 186 | } 187 | 188 | public void setV5(int v5) { 189 | this.v5 = v5; 190 | } 191 | 192 | public long getV6() { 193 | return v6; 194 | } 195 | 196 | public void setV6(long v6) { 197 | this.v6 = v6; 198 | } 199 | 200 | public int getV7() { 201 | return v7; 202 | } 203 | 204 | public void setV7(int v7) { 205 | this.v7 = v7; 206 | } 207 | 208 | public long getV8() { 209 | return v8; 210 | } 211 | 212 | public void setV8(long v8) { 213 | this.v8 = v8; 214 | } 215 | 216 | public int getV9() { 217 | return v9; 218 | } 219 | 220 | public void setV9(int v9) { 221 | this.v9 = v9; 222 | } 223 | 224 | public long getV10() { 225 | return v10; 226 | } 227 | 228 | public void setV10(long v10) { 229 | this.v10 = v10; 230 | } 231 | 232 | public int getV11() { 233 | return v11; 234 | } 235 | 236 | public void setV11(int v11) { 237 | this.v11 = v11; 238 | } 239 | 240 | public long getV12() { 241 | return v12; 242 | } 243 | 244 | public void setV12(long v12) { 245 | this.v12 = v12; 246 | } 247 | 248 | public boolean isV13() { 249 | return v13; 250 | } 251 | 252 | public void setV13(boolean v13) { 253 | this.v13 = v13; 254 | } 255 | 256 | public String getV14() { 257 | return v14; 258 | } 259 | 260 | public void setV14(String v14) { 261 | this.v14 = v14; 262 | } 263 | 264 | public ByteString getV15() { 265 | return v15; 266 | } 267 | 268 | public void setV15(ByteString v15) { 269 | this.v15 = v15; 270 | } 271 | 272 | public Status getV16() { 273 | return v16; 274 | } 275 | 276 | public void setV16(Status v16) { 277 | this.v16 = v16; 278 | } 279 | 280 | public List getRv1() { 281 | return rv1; 282 | } 283 | 284 | public void setRv1(List rv1) { 285 | this.rv1 = rv1; 286 | } 287 | 288 | public List getRv2() { 289 | return rv2; 290 | } 291 | 292 | public void setRv2(List rv2) { 293 | this.rv2 = rv2; 294 | } 295 | 296 | public List getRv3() { 297 | return rv3; 298 | } 299 | 300 | public void setRv3(List rv3) { 301 | this.rv3 = rv3; 302 | } 303 | 304 | public List getRv4() { 305 | return rv4; 306 | } 307 | 308 | public void setRv4(List rv4) { 309 | this.rv4 = rv4; 310 | } 311 | 312 | public List getRv5() { 313 | return rv5; 314 | } 315 | 316 | public void setRv5(List rv5) { 317 | this.rv5 = rv5; 318 | } 319 | 320 | public List getRv6() { 321 | return rv6; 322 | } 323 | 324 | public void setRv6(List rv6) { 325 | this.rv6 = rv6; 326 | } 327 | 328 | public List getRv7() { 329 | return rv7; 330 | } 331 | 332 | public void setRv7(List rv7) { 333 | this.rv7 = rv7; 334 | } 335 | 336 | public List getRv8() { 337 | return rv8; 338 | } 339 | 340 | public void setRv8(List rv8) { 341 | this.rv8 = rv8; 342 | } 343 | 344 | public List getRv9() { 345 | return rv9; 346 | } 347 | 348 | public void setRv9(List rv9) { 349 | this.rv9 = rv9; 350 | } 351 | 352 | public List getRv10() { 353 | return rv10; 354 | } 355 | 356 | public void setRv10(List rv10) { 357 | this.rv10 = rv10; 358 | } 359 | 360 | public List getRv11() { 361 | return rv11; 362 | } 363 | 364 | public void setRv11(List rv11) { 365 | this.rv11 = rv11; 366 | } 367 | 368 | public List getRv12() { 369 | return rv12; 370 | } 371 | 372 | public void setRv12(List rv12) { 373 | this.rv12 = rv12; 374 | } 375 | 376 | public List getRv13() { 377 | return rv13; 378 | } 379 | 380 | public void setRv13(List rv13) { 381 | this.rv13 = rv13; 382 | } 383 | 384 | public List getRv14() { 385 | return rv14; 386 | } 387 | 388 | public void setRv14(List rv14) { 389 | this.rv14 = rv14; 390 | } 391 | 392 | public List getRv15() { 393 | return rv15; 394 | } 395 | 396 | public void setRv15(List rv15) { 397 | this.rv15 = rv15; 398 | } 399 | 400 | public List getRv16() { 401 | return rv16; 402 | } 403 | 404 | public void setRv16(List rv16) { 405 | this.rv16 = rv16; 406 | } 407 | 408 | public MultiNumber getMultiNumber() { 409 | return multiNumber; 410 | } 411 | 412 | public void setMultiNumber(MultiNumber multiNumber) { 413 | this.multiNumber = multiNumber; 414 | } 415 | 416 | public List getRepMultiNumbers() { 417 | return repMultiNumbers; 418 | } 419 | 420 | public void setRepMultiNumbers(List rMultiNumbers) { 421 | repMultiNumbers = rMultiNumbers; 422 | } 423 | 424 | public String getNonRepeatableFieldWithSuffixList() { 425 | return nonRepeatableFieldWithSuffixList; 426 | } 427 | 428 | public void setNonRepeatableFieldWithSuffixList(String nonRepeatableFieldWithSuffixList) { 429 | this.nonRepeatableFieldWithSuffixList = nonRepeatableFieldWithSuffixList; 430 | } 431 | 432 | public User getUser() { 433 | return user; 434 | } 435 | 436 | public void setUser(User user) { 437 | this.user = user; 438 | } 439 | 440 | public List getUsers() { 441 | return users; 442 | } 443 | 444 | public void setUsers(List users) { 445 | this.users = users; 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /usage/src/main/proto/EnumPostfixOverride.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "no.entur.mapstruct.example"; 4 | option java_outer_classname = "EnumPostfixOverrideProtos"; 5 | 6 | enum EnumPostfixOverrideValuesDTO { 7 | ENUM_POSTFIX_OVERRIDE_VALUES_D_T_O_INVALID = 0; 8 | ENUM_POSTFIX_OVERRIDE_VALUES_D_T_O_ONE = 1; 9 | ENUM_POSTFIX_OVERRIDE_VALUES_D_T_O_TWO = 2; 10 | } 11 | -------------------------------------------------------------------------------- /usage/src/main/proto/User.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "no.entur.mapstruct.example"; 4 | option java_outer_classname = "UserProtos"; 5 | 6 | enum EnumStatus { 7 | ENUM_STATUS_UNSPECIFIED = 0; 8 | ENUM_STATUS_STARTED = 1; 9 | ENUM_STATUS_STOPPED = 2; 10 | } 11 | 12 | message MultiNumberDTO { 13 | oneof number { 14 | int32 integer = 1; 15 | double floating = 2; 16 | } 17 | } 18 | 19 | message UserDTO { 20 | oneof OneOfDepartments { 21 | DepartmentDTO fire_department = 123; 22 | DepartmentDTO police_department = 124; 23 | } 24 | 25 | string id = 1; 26 | string email = 2; 27 | repeated DepartmentDTO main_departments = 4; 28 | repeated DepartmentDTO departments = 5; 29 | 30 | double v1 = 6; 31 | float v2 = 7; 32 | int32 v3 = 8; 33 | int64 v4 = 9; 34 | uint32 v5 = 10; 35 | uint64 v6 = 11; 36 | sint32 v7 = 12; 37 | sint64 v8 = 13; 38 | fixed32 v9 = 14; 39 | fixed64 v10 = 15; 40 | sfixed32 v11 = 16; 41 | sfixed64 v12 = 17; 42 | bool v13 = 18; 43 | string v14 = 19; 44 | bytes v15 = 20; 45 | 46 | EnumStatus v16 = 21; 47 | // google.protobuf.Any v17 = 22; 48 | 49 | // oneof v18 { 50 | // int32 oneofv1 = 23; 51 | // double oneofv2 = 24; 52 | // } 53 | 54 | map stringMap = 25; 55 | map entityMap = 26; 56 | 57 | MultiNumberDTO multi_number = 27; 58 | 59 | UserDTO user = 28; 60 | 61 | repeated double rv1 = 100; 62 | repeated float rv2 = 101; 63 | repeated int32 rv3 = 102; 64 | repeated int64 rv4 = 103; 65 | repeated uint32 rv5 = 104; 66 | repeated uint64 rv6 = 105; 67 | repeated sint32 rv7 = 106; 68 | repeated sint64 rv8 = 107; 69 | repeated fixed32 rv9 = 108; 70 | repeated fixed64 rv10 = 109; 71 | repeated sfixed32 rv11 = 110; 72 | repeated sfixed64 rv12 = 111; 73 | repeated bool rv13 = 112; 74 | repeated string rv14 = 113; 75 | repeated bytes rv15 = 114; 76 | 77 | repeated EnumStatus rv16 = 115; 78 | // repeated google.protobuf.Any rv17 = 116; 79 | 80 | repeated MultiNumberDTO rep_multi_numbers = 120; 81 | 82 | string non_repeatable_field_with_suffix_list = 121; 83 | 84 | repeated UserDTO users = 122; 85 | 86 | } 87 | 88 | enum PermissionDTO { 89 | PERMISSION_D_T_O_UNSPECIFIED = 0; 90 | PERMISSION_D_T_O_USER = 1; 91 | PERMISSION_D_T_O_ADMIN = 2; 92 | PERMISSION_D_T_O_NONE = 3; 93 | } 94 | 95 | message DepartmentDTO { 96 | string name = 1; 97 | } 98 | 99 | -------------------------------------------------------------------------------- /usage/src/test/java/no/entur/mapstruct/example/MappingTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) 3 | * and/or other contributors as indicated by the @authors tag. See the 4 | * copyright.txt file in the distribution for a full listing of all 5 | * contributors. 6 | *

7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | *

11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | *

13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | package no.entur.mapstruct.example; 20 | 21 | /*- 22 | * #%L 23 | * protobuf-usage 24 | * %% 25 | * Copyright (C) 2019 - 2020 Entur 26 | * %% 27 | * Licensed under the EUPL, Version 1.1 or – as soon they will be 28 | * approved by the European Commission - subsequent versions of the 29 | * EUPL (the "Licence"); 30 | * 31 | * You may not use this work except in compliance with the Licence. 32 | * You may obtain a copy of the Licence at: 33 | * 34 | * http://ec.europa.eu/idabc/eupl5 35 | * 36 | * Unless required by applicable law or agreed to in writing, software 37 | * distributed under the Licence is distributed on an "AS IS" basis, 38 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 39 | * See the Licence for the specific language governing permissions and 40 | * limitations under the Licence. 41 | * #L% 42 | */ 43 | 44 | import static org.junit.jupiter.api.Assertions.assertEquals; 45 | import static org.junit.jupiter.api.Assertions.assertNotNull; 46 | import static org.junit.jupiter.api.Assertions.assertTrue; 47 | 48 | import java.util.Arrays; 49 | 50 | import org.junit.jupiter.api.Test; 51 | 52 | import com.google.protobuf.ByteString; 53 | import com.google.protobuf.InvalidProtocolBufferException; 54 | 55 | import no.entur.mapstruct.example.EnumPostfixOverrideProtos.EnumPostfixOverrideValuesDTO; 56 | import no.entur.mapstruct.example.UserProtos.UserDTO; 57 | import no.entur.mapstruct.spi.protobuf.Department; 58 | import no.entur.mapstruct.spi.protobuf.EnumPostfixOverrideValues; 59 | import no.entur.mapstruct.spi.protobuf.MultiNumber; 60 | import no.entur.mapstruct.spi.protobuf.Status; 61 | import no.entur.mapstruct.spi.protobuf.User; 62 | 63 | public class MappingTest { 64 | 65 | @Test 66 | public void testMapAllFields() throws InvalidProtocolBufferException { 67 | User user = generateUser(); 68 | 69 | UserDTO dto = UserMapper.INSTANCE.map(user); 70 | UserDTO deserialized = UserDTO.parseFrom(dto.toByteArray()); 71 | User back = UserMapper.INSTANCE.map(deserialized); 72 | 73 | assertUser(user, back); 74 | } 75 | 76 | @Test 77 | public void testNulls() throws InvalidProtocolBufferException { 78 | User user = new User(); 79 | user.setEmail("test"); 80 | 81 | UserDTO dto = UserMapper.INSTANCE.map(user); 82 | UserDTO deserialized = UserDTO.parseFrom(dto.toByteArray()); 83 | User back = UserMapper.INSTANCE.map(deserialized); 84 | 85 | assertEquals(null, back.getId()); 86 | assertEquals("test", back.getEmail()); 87 | assertEquals(null, back.getV16()); 88 | } 89 | 90 | @Test 91 | public void testMaps() throws InvalidProtocolBufferException { 92 | User user = new User(); 93 | user.getStringMap().put("key1", "value1"); 94 | user.getStringMap().put("key2", "value2"); 95 | user.getEntityMap().put("entityKey1", new Department("DepartmentName1")); 96 | user.getEntityMap().put("entityKey2", new Department("DepartmentName2")); 97 | 98 | UserDTO dto = UserMapper.INSTANCE.map(user); 99 | UserDTO deserialized = UserDTO.parseFrom(dto.toByteArray()); 100 | User back = UserMapper.INSTANCE.map(deserialized); 101 | 102 | assertTrue(back.getStringMap().containsKey("key1")); 103 | assertEquals("value1", back.getStringMap().get("key1")); 104 | assertTrue(back.getStringMap().containsKey("key2")); 105 | assertEquals("value2", back.getStringMap().get("key2")); 106 | 107 | assertTrue(back.getEntityMap().containsKey("entityKey1")); 108 | assertEquals("DepartmentName1", back.getEntityMap().get("entityKey1").getName()); 109 | assertTrue(back.getEntityMap().containsKey("entityKey2")); 110 | assertEquals("DepartmentName2", back.getEntityMap().get("entityKey2").getName()); 111 | 112 | } 113 | 114 | private User generateUser() { 115 | User user = new User(); 116 | 117 | user.setId(null); 118 | user.setEmail("test"); 119 | user.getMainDepartments().add(new Department("SALES")); 120 | user.getDepartments().add(new Department("AFTER_MARKET")); 121 | 122 | user.setV1(1.0); 123 | user.setV2(2); 124 | user.setV3(3); 125 | user.setV4(4); 126 | user.setV5(5); 127 | user.setV6(6); 128 | user.setV7(7); 129 | user.setV8(8); 130 | user.setV9(9); 131 | user.setV10(10); 132 | user.setV11(11); 133 | user.setV12(12); 134 | user.setV13(true); 135 | user.setV14("some string"); 136 | user.setV15(ByteString.copyFromUtf8("byte string")); 137 | user.setV16(Status.STARTED); 138 | 139 | user.setRv1(Arrays.asList(1.0)); 140 | user.setRv2(Arrays.asList(2.0f)); 141 | user.setRv3(Arrays.asList(3)); 142 | user.setRv4(Arrays.asList(4L)); 143 | user.setRv5(Arrays.asList(5)); 144 | user.setRv6(Arrays.asList(6L)); 145 | user.setRv7(Arrays.asList(7)); 146 | user.setRv8(Arrays.asList(8L)); 147 | user.setRv9(Arrays.asList(9)); 148 | user.setRv10(Arrays.asList(10L)); 149 | user.setRv11(Arrays.asList(11)); 150 | user.setRv12(Arrays.asList(12L)); 151 | user.setRv13(Arrays.asList(true)); 152 | user.setRv14(Arrays.asList("some string")); 153 | user.setRv15(Arrays.asList(ByteString.copyFromUtf8("some byte string"))); 154 | user.setRv16(Arrays.asList(Status.STARTED)); 155 | 156 | MultiNumber mm = new MultiNumber(); 157 | mm.setNumber(1); 158 | 159 | user.setMultiNumber(mm); 160 | user.setRepMultiNumbers(Arrays.asList(mm)); 161 | 162 | user.setPoliceDepartment(new Department("POLICE")); 163 | 164 | return user; 165 | } 166 | 167 | private void assertUser(User orig, User back) { 168 | assertEquals(orig.getId(), back.getId()); 169 | assertEquals(orig.getEmail(), back.getEmail()); 170 | 171 | assertEquals(orig.getMainDepartments().size(), back.getMainDepartments().size()); 172 | assertEquals(orig.getMainDepartments().get(0).getName(), back.getMainDepartments().get(0).getName()); 173 | 174 | assertEquals(orig.getDepartments().size(), back.getDepartments().size()); 175 | assertEquals(orig.getDepartments().get(0).getName(), back.getDepartments().get(0).getName()); 176 | 177 | assertEquals(orig.getV1(), back.getV1(), 0.1); 178 | assertEquals(orig.getV2(), back.getV2(), 0.1); 179 | assertEquals(orig.getV3(), back.getV3()); 180 | assertEquals(orig.getV4(), back.getV4()); 181 | assertEquals(orig.getV5(), back.getV5()); 182 | assertEquals(orig.getV6(), back.getV6()); 183 | assertEquals(orig.getV7(), back.getV7()); 184 | assertEquals(orig.getV8(), back.getV8()); 185 | assertEquals(orig.getV9(), back.getV9()); 186 | assertEquals(orig.getV10(), back.getV10()); 187 | assertEquals(orig.getV11(), back.getV11()); 188 | assertEquals(orig.getV12(), back.getV12()); 189 | 190 | assertEquals(orig.isV13(), back.isV13()); 191 | 192 | assertEquals(orig.getV14(), back.getV14()); 193 | assertEquals(orig.getV15(), back.getV15()); 194 | assertEquals(orig.getV16(), back.getV16()); 195 | assertEquals(orig.getV16(), back.getV16()); 196 | 197 | assertEquals(orig.getRv1().size(), back.getRv1().size()); 198 | assertEquals(orig.getRv1().get(0), back.getRv1().get(0), 0.1); 199 | 200 | assertEquals(orig.getRv2().size(), back.getRv2().size()); 201 | assertEquals(orig.getRv2().get(0), back.getRv2().get(0), 0.1); 202 | 203 | assertEquals(orig.getRv3().size(), back.getRv3().size()); 204 | assertEquals(orig.getRv3().get(0), back.getRv3().get(0)); 205 | 206 | assertEquals(orig.getRv4().size(), back.getRv4().size()); 207 | assertEquals(orig.getRv4().get(0), back.getRv4().get(0)); 208 | 209 | assertEquals(orig.getRv5().size(), back.getRv5().size()); 210 | assertEquals(orig.getRv5().get(0), back.getRv5().get(0)); 211 | 212 | assertEquals(orig.getRv6().size(), back.getRv6().size()); 213 | assertEquals(orig.getRv6().get(0), back.getRv6().get(0)); 214 | 215 | assertEquals(orig.getRv7().size(), back.getRv7().size()); 216 | assertEquals(orig.getRv7().get(0), back.getRv7().get(0)); 217 | 218 | assertEquals(orig.getRv8().size(), back.getRv8().size()); 219 | assertEquals(orig.getRv8().get(0), back.getRv8().get(0)); 220 | 221 | assertEquals(orig.getRv9().size(), back.getRv9().size()); 222 | assertEquals(orig.getRv9().get(0), back.getRv9().get(0)); 223 | 224 | assertEquals(orig.getRv10().size(), back.getRv10().size()); 225 | assertEquals(orig.getRv10().get(0), back.getRv10().get(0)); 226 | 227 | assertEquals(orig.getRv11().size(), back.getRv11().size()); 228 | assertEquals(orig.getRv11().get(0), back.getRv11().get(0)); 229 | 230 | assertEquals(orig.getRv12().size(), back.getRv12().size()); 231 | assertEquals(orig.getRv12().get(0), back.getRv12().get(0)); 232 | 233 | assertEquals(orig.getRv13().size(), back.getRv13().size()); 234 | assertEquals(orig.getRv13().get(0), back.getRv13().get(0)); 235 | 236 | assertEquals(orig.getRv14().size(), back.getRv14().size()); 237 | assertEquals(orig.getRv14().get(0), back.getRv14().get(0)); 238 | 239 | assertEquals(orig.getRv15().size(), back.getRv15().size()); 240 | assertEquals(orig.getRv15().get(0), back.getRv15().get(0)); 241 | 242 | assertEquals(orig.getRv16().size(), back.getRv16().size()); 243 | assertEquals(orig.getRv16().get(0), back.getRv16().get(0)); 244 | 245 | assertNotNull(back.getPoliceDepartment()); 246 | assertEquals(back.getPoliceDepartment().getName(), "POLICE"); 247 | // WILL FAIL: Mapstruct cannot handle presence checker on oneOfs : assertNull(back.getFireDepartment()); 248 | 249 | } 250 | 251 | @Test 252 | public void testEnumPostfixOverride() { 253 | EnumPostfixOverrideValues enumValue = EnumPostfixOverrideValues.TWO; 254 | EnumPostfixOverrideValuesDTO dto = EnumPostfixOverrideMapper.INSTANCE.map(enumValue); 255 | EnumPostfixOverrideValues back = EnumPostfixOverrideMapper.INSTANCE.map(dto); 256 | 257 | assertEquals(enumValue, back); 258 | } 259 | } 260 | --------------------------------------------------------------------------------