-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
ConstructorsDeclarationGroupingCheck.java
162 lines (142 loc) · 5.94 KB
/
ConstructorsDeclarationGroupingCheck.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
///////////////////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
// Copyright (C) 2001-2024 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
///////////////////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.checks.coding;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.puppycrawl.tools.checkstyle.StatelessCheck;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
/**
* <p>
* Checks that all constructors are grouped together.
* If there is any non-constructor code separating constructors,
* this check identifies and logs a violation for those ungrouped constructors.
* The violation message will specify the line number of the last grouped constructor.
* Comments between constructors are allowed.
* </p>
* <p>
* Rationale: Grouping constructors together in a class improves code readability
* and maintainability. It allows developers to easily understand
* the different ways an object can be instantiated
* and the tasks performed by each constructor.
* </p>
* <p>
* Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
* </p>
* <p>
* Violation Message Keys:
* </p>
* <ul>
* <li>
* {@code constructors.declaration.grouping}
* </li>
* </ul>
*
* @since 10.17.0
*/
@StatelessCheck
public class ConstructorsDeclarationGroupingCheck extends AbstractCheck {
/**
* A key is pointing to the warning message text in "messages.properties"
* file.
*/
public static final String MSG_KEY = "constructors.declaration.grouping";
@Override
public int[] getDefaultTokens() {
return getRequiredTokens();
}
@Override
public int[] getAcceptableTokens() {
return getRequiredTokens();
}
@Override
public int[] getRequiredTokens() {
return new int[] {
TokenTypes.CLASS_DEF,
TokenTypes.ENUM_DEF,
TokenTypes.RECORD_DEF,
};
}
@Override
public void visitToken(DetailAST ast) {
// list of all child ASTs
final List<DetailAST> children = getChildList(ast);
// find first constructor
final DetailAST firstConstructor = children.stream()
.filter(ConstructorsDeclarationGroupingCheck::isConstructor)
.findFirst()
.orElse(null);
if (firstConstructor != null) {
// get all children AST after the first constructor
final List<DetailAST> childrenAfterFirstConstructor =
children.subList(children.indexOf(firstConstructor), children.size());
// find the first index of non-constructor AST after the first constructor, if present
final Optional<Integer> indexOfFirstNonConstructor = childrenAfterFirstConstructor
.stream()
.filter(currAst -> !isConstructor(currAst))
.findFirst()
.map(DetailAST.class::cast)
.map(children::indexOf);
// list of all children after first non-constructor AST
final List<DetailAST> childrenAfterFirstNonConstructor = indexOfFirstNonConstructor
.map(index -> children.subList(index, children.size()))
.orElseGet(ArrayList::new);
// create a list of all constructors that are not grouped to log
final List<DetailAST> constructorsToLog = childrenAfterFirstNonConstructor.stream()
.filter(ConstructorsDeclarationGroupingCheck::isConstructor)
.collect(Collectors.toUnmodifiableList());
// find the last grouped constructor
final DetailAST lastGroupedConstructor = childrenAfterFirstConstructor.stream()
.takeWhile(ConstructorsDeclarationGroupingCheck::isConstructor)
.reduce((first, second) -> second)
.orElse(firstConstructor);
// log all constructors that are not grouped
constructorsToLog
.forEach(ctor -> log(ctor, MSG_KEY, lastGroupedConstructor.getLineNo()));
}
}
/**
* Get a list of all children of the given AST.
*
* @param ast the AST to get children of
* @return a list of all children of the given AST
*/
private static List<DetailAST> getChildList(DetailAST ast) {
final List<DetailAST> children = new ArrayList<>();
DetailAST child = ast.findFirstToken(TokenTypes.OBJBLOCK).getFirstChild();
while (child != null) {
children.add(child);
child = child.getNextSibling();
}
return children;
}
/**
* Check if the given AST is a constructor.
*
* @param ast the AST to check
* @return true if the given AST is a constructor, false otherwise
*/
private static boolean isConstructor(DetailAST ast) {
return ast.getType() == TokenTypes.CTOR_DEF
|| ast.getType() == TokenTypes.COMPACT_CTOR_DEF;
}
}