Skip to content

Commit dc552b1

Browse files
authored
Merge pull request #1437 from jeffgbutler/refactor-javamerger
Java Merger Enhancements - Support Records and Enums
2 parents 345d7f9 + a9adf72 commit dc552b1

16 files changed

Lines changed: 1109 additions & 230 deletions

core/mybatis-generator-core/src/main/java/org/mybatis/generator/internal/JavaMergingShellCallback.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.jspecify.annotations.Nullable;
2121
import org.mybatis.generator.exception.ShellException;
22+
import org.mybatis.generator.merge.java.EclipseOrderedPrinterConfiguration;
2223
import org.mybatis.generator.merge.java.JavaFileMerger;
2324

2425
public class JavaMergingShellCallback extends DefaultShellCallback {
@@ -35,6 +36,7 @@ public boolean isMergeSupported() {
3536
@Override
3637
public String mergeJavaFile(String newFileSource, File existingFile,
3738
String[] javadocTags, @Nullable String fileEncoding) throws ShellException {
38-
return JavaFileMerger.getMergedSource(newFileSource, existingFile, javadocTags, fileEncoding);
39+
JavaFileMerger javaFileMerger = new JavaFileMerger(new EclipseOrderedPrinterConfiguration());
40+
return javaFileMerger.getMergedSource(newFileSource, existingFile, fileEncoding);
3941
}
4042
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright 2006-2026 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.generator.merge.java;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.function.Function;
21+
import java.util.stream.Stream;
22+
23+
import com.github.javaparser.ast.body.BodyDeclaration;
24+
import com.github.javaparser.ast.body.EnumConstantDeclaration;
25+
import com.github.javaparser.ast.body.TypeDeclaration;
26+
import com.github.javaparser.ast.comments.Comment;
27+
import com.github.javaparser.ast.expr.AnnotationExpr;
28+
import com.github.javaparser.ast.expr.Expression;
29+
import com.github.javaparser.ast.expr.MemberValuePair;
30+
import com.github.javaparser.ast.expr.StringLiteralExpr;
31+
import org.mybatis.generator.api.MyBatisGenerator;
32+
import org.mybatis.generator.config.MergeConstants;
33+
34+
/**
35+
* Inspect a TypeDeclaration looking for members that should be merged. We only look for immediate children of the
36+
* TypeDeclaration, we do not recurse into nested types. There are three resulting lists of members:
37+
*
38+
* <ol>
39+
* <li>customBodyMembers: members that should be merged with the target file because they are not generated by
40+
* MyBatis Generator</li>
41+
* <li>doNotDeleteBodyMembers: members that were originally generated by MyBatis Generator, but marked
42+
* "do_not_delete_during_merge". This means that any matching member in the newly generated file should be removed,
43+
* and these members should be merged. Currently, the only known use of this feature is the legacy Example classes.
44+
* Those classes have a "Criteria" inner class that can be modified as an extension point.</li>
45+
* <li>customEnumConstants: enum constants that should be merged with the target file because they are not
46+
* generated by MyBatis Generator</li>
47+
* </ol>
48+
*
49+
* <p>If all lists are empty, there is no need to merge any members into a new file, and the new file can be used as is.
50+
*/
51+
public class CustomMemberGatherer {
52+
private final List<BodyDeclaration<?>> customBodyMembers = new ArrayList<>();
53+
private final List<BodyDeclaration<?>> doNotDeleteBodyMembers = new ArrayList<>();
54+
private final List<EnumConstantDeclaration> customEnumConstants = new ArrayList<>();
55+
56+
public CustomMemberGatherer(TypeDeclaration<?> typeDeclaration) {
57+
typeDeclaration.getMembers().forEach(this::gatherCustomBodyMemberIfNeeded);
58+
59+
if (typeDeclaration.isEnumDeclaration()) {
60+
typeDeclaration.asEnumDeclaration().getEntries().forEach(this::gatherCustomEnumConstantIfNeeded);
61+
}
62+
}
63+
64+
public boolean hasAnyMembersToMerge() {
65+
return !customBodyMembers.isEmpty() || !doNotDeleteBodyMembers.isEmpty() || !customEnumConstants.isEmpty();
66+
}
67+
68+
public Stream<BodyDeclaration<?>> allCustomBodyMembers() {
69+
return Stream.of(customBodyMembers.stream(), doNotDeleteBodyMembers.stream())
70+
.flatMap(Function.identity());
71+
}
72+
73+
public Stream<BodyDeclaration<?>> doNotDeleteBodyMembers() {
74+
return doNotDeleteBodyMembers.stream();
75+
}
76+
77+
public Stream<EnumConstantDeclaration> customEnumConstants() {
78+
return customEnumConstants.stream();
79+
}
80+
81+
private void gatherCustomBodyMemberIfNeeded(BodyDeclaration<?> member) {
82+
if (hasGeneratedAnnotation(member)) {
83+
return;
84+
}
85+
86+
GeneratedType generatedType = checkForGeneratedJavadocTag(member);
87+
if (generatedType == GeneratedType.NOT_GENERATED) {
88+
customBodyMembers.add(member);
89+
} else if (generatedType == GeneratedType.GENERATED_KEEP) {
90+
doNotDeleteBodyMembers.add(member);
91+
}
92+
}
93+
94+
private void gatherCustomEnumConstantIfNeeded(EnumConstantDeclaration member) {
95+
if (hasGeneratedAnnotation(member)) {
96+
return;
97+
}
98+
99+
GeneratedType generatedType = checkForGeneratedJavadocTag(member);
100+
if (generatedType == GeneratedType.NOT_GENERATED || generatedType == GeneratedType.GENERATED_KEEP) {
101+
customEnumConstants.add(member);
102+
}
103+
}
104+
105+
private boolean hasGeneratedAnnotation(BodyDeclaration<?> member) {
106+
return member.getAnnotations().stream()
107+
.anyMatch(this::isOurGeneratedAnnotation);
108+
}
109+
110+
private boolean isOurGeneratedAnnotation(AnnotationExpr annotationExpr) {
111+
if (!isGeneratedAnnotation(annotationExpr)) {
112+
return false;
113+
}
114+
115+
if (annotationExpr.isSingleMemberAnnotationExpr()) {
116+
Expression value = annotationExpr.asSingleMemberAnnotationExpr().getMemberValue();
117+
if (value.isStringLiteralExpr()) {
118+
return annotationValueMatchesMyBatisGenerator(value.asStringLiteralExpr());
119+
}
120+
} else if (annotationExpr.isNormalAnnotationExpr()) {
121+
return annotationExpr.asNormalAnnotationExpr().getPairs().stream()
122+
.filter(this::isValuePair)
123+
.map(MemberValuePair::getValue)
124+
.filter(Expression::isStringLiteralExpr)
125+
.map(Expression::asStringLiteralExpr)
126+
.findFirst()
127+
.map(this::annotationValueMatchesMyBatisGenerator)
128+
.orElse(false);
129+
}
130+
131+
return false;
132+
}
133+
134+
private boolean isGeneratedAnnotation(AnnotationExpr annotationExpr) {
135+
String annotationName = annotationExpr.getNameAsString();
136+
// Check for @Generated annotation (both javax and jakarta packages)
137+
return "Generated".equals(annotationName) //$NON-NLS-1$
138+
|| "javax.annotation.Generated".equals(annotationName) //$NON-NLS-1$
139+
|| "jakarta.annotation.Generated".equals(annotationName); //$NON-NLS-1$
140+
}
141+
142+
private boolean isValuePair(MemberValuePair pair) {
143+
return pair.getName().asString().equals("value"); //$NON-NLS-1$
144+
}
145+
146+
private boolean annotationValueMatchesMyBatisGenerator(StringLiteralExpr expr) {
147+
return expr.asString().equals(MyBatisGenerator.class.getName());
148+
}
149+
150+
private GeneratedType checkForGeneratedJavadocTag(BodyDeclaration<?> member) {
151+
return member.getComment()
152+
.map(Comment::getContent)
153+
.map(this::checkJavadocTag)
154+
.orElse(GeneratedType.NOT_GENERATED);
155+
}
156+
157+
// Check if the comment contains any of the javadoc tags
158+
private GeneratedType checkJavadocTag(String comment) {
159+
for (String tag : MergeConstants.getOldElementTags()) {
160+
if (comment.contains(tag)) {
161+
if (comment.contains(MergeConstants.DO_NOT_DELETE_DURING_MERGE)) {
162+
return GeneratedType.GENERATED_KEEP;
163+
} else {
164+
return GeneratedType.GENERATED_REMOVE;
165+
}
166+
}
167+
}
168+
return GeneratedType.NOT_GENERATED;
169+
}
170+
171+
private enum GeneratedType {
172+
NOT_GENERATED,
173+
GENERATED_REMOVE,
174+
GENERATED_KEEP
175+
}
176+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2006-2026 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.generator.merge.java;
17+
18+
import com.github.javaparser.printer.configuration.DefaultConfigurationOption;
19+
import com.github.javaparser.printer.configuration.DefaultPrinterConfiguration;
20+
import com.github.javaparser.printer.configuration.imports.EclipseImportOrderingStrategy;
21+
22+
/**
23+
* Printer configuration that orders imports in "Eclipse" order (statics first, etc.).
24+
*/
25+
public class EclipseOrderedPrinterConfiguration extends DefaultPrinterConfiguration {
26+
public EclipseOrderedPrinterConfiguration() {
27+
addOption(new DefaultConfigurationOption(DefaultPrinterConfiguration.ConfigOption.SORT_IMPORTS_STRATEGY,
28+
new EclipseImportOrderingStrategy()));
29+
addOption(new DefaultConfigurationOption(DefaultPrinterConfiguration.ConfigOption.ORDER_IMPORTS, true));
30+
}
31+
}

0 commit comments

Comments
 (0)