2
* Copyright (c) 2002-2009 Gargoyle Software Inc.
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
* http://www.apache.org/licenses/LICENSE-2.0
9
* Unless required by applicable law or agreed to in writing, software
10
* distributed under the License is distributed on an "AS IS" BASIS,
11
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
* See the License for the specific language governing permissions and
13
* limitations under the License.
15
package com.gargoylesoftware.htmlunit;
18
* Test of coding style using Eclipse JDT.
19
* Currently checks for missing @Override annotations.
21
* To run it, you need:
23
* <li>org.eclipse.core.contenttype_x.jar</li>
24
* <li>org.eclipse.core.jobs_x.jar</li>
25
* <li>org.eclipse.core.resources_x.jar</li>
26
* <li>org.eclipse.core.runtime_x.jar</li>
27
* <li>org.eclipse.equinox.preferences_x.jar</li>
28
* <li>org.eclipse.osgi_x.jar</li>
31
* @version $Revision: 4833 $
32
* @author Ahmed Ashour
34
public class CodeChecker {
37
private List<String> errors_ = new ArrayList<String>();
38
private ASTParser parser_ = ASTParser.newParser(AST.JLS3);
42
final StringBuilder sb = new StringBuilder();
43
for (final String error : errors_) {
44
sb.append("\n" + error);
47
final int errorsNumber = errors_.size();
48
if (errorsNumber == 1) {
49
fail("CodeChecker error: " + sb);
51
else if (errorsNumber > 1) {
52
fail("CodeChecker " + errorsNumber + " errors: " + sb);
56
private void addFailure(final String error) {
61
public void codeChecker() throws Exception {
62
process(new File("src/main/java"));
63
process(new File("src/test/java"));
66
private void process(final File dir) throws Exception {
67
for (final File file : dir.listFiles()) {
68
if (file.isDirectory() && !file.getName().equals(".svn")) {
72
if (file.getName().endsWith(".java")) {
73
final List<String> lines = CodeStyleTest.getLines(file);
81
* Checks if the given file has any missing @Override annotation.
84
* @throws Exception If an error occurs
86
private void checkOverride(final List<String> lines) throws Exception {
87
final CharArrayWriter writer = new CharArrayWriter();
88
for (final String line : lines) {
92
parser_.setSource(writer.toCharArray());
93
final CompilationUnit cu = (CompilationUnit) parser_.createAST(null);
95
for (final Object type : cu.types()) {
96
if (type instanceof TypeDeclaration) {
97
checkOverride((TypeDeclaration) type);
102
private void checkOverride(final TypeDeclaration type) throws Exception {
103
for (final TypeDeclaration tt : type.getTypes()) {
106
for (final MethodDeclaration method : type.getMethods()) {
107
boolean isPrivate = false;
108
for (final Object m : method.modifiers()) {
109
if (m instanceof Modifier && ((Modifier) m).isPrivate()) {
114
if (!isPrivate && !method.isConstructor() && !hasOverride(method) && shouldOverride(method)) {
115
final String className = getFullyQualifiedName((TypeDeclaration) method.getParent()).replace('$', '.');
116
addFailure("@Override is not defined for " + className + "." + method.getName());
121
private static boolean hasOverride(final MethodDeclaration method) {
122
for (final Object modifier : method.modifiers()) {
123
if (modifier instanceof MarkerAnnotation) {
124
final String name = ((MarkerAnnotation) modifier).getTypeName().getFullyQualifiedName();
125
if (name.equals("Override") || name.equals("java.lang.Override")) {
133
private static String getFullyQualifiedName(final TypeDeclaration type) {
134
String name = type.getName().getFullyQualifiedName();
135
final CompilationUnit root = (CompilationUnit) type.getRoot();
136
for (ASTNode t = type.getParent(); t != root; t = t.getParent()) {
137
name = ((TypeDeclaration) t).getName().getFullyQualifiedName() + '$' + name;
140
if (root.getPackage() != null) {
141
final String packageName = root.getPackage().getName().getFullyQualifiedName();
142
name = packageName + '.' + name;
147
private static boolean shouldOverride(final MethodDeclaration method) throws Exception {
148
final TypeDeclaration type = (TypeDeclaration) method.getParent();
149
final Class< ? > klass = Class.forName(getFullyQualifiedName(type));
150
final Class< ? >[] types = new Class[method.parameters().size()];
151
for (int i = 0; i < types.length; i++) {
152
final SingleVariableDeclaration parameter = (SingleVariableDeclaration) method.parameters().get(i);
153
types[i] = getClassOf(parameter.getType(), method.isVarargs() && i == method.parameters().size() - 1);
156
// Check we have the method
157
assertNotNull(klass.getDeclaredMethod(method.getName().getFullyQualifiedName(), types));
159
for (Class< ? > c = klass.getSuperclass(); c != null; c = c.getSuperclass()) {
161
c.getDeclaredMethod(method.getName().getFullyQualifiedName(), types);
164
catch (final NoSuchMethodException e) {
171
private static Class< ? > getClassOf(final Type type, final boolean isVararg) {
172
Class< ? > klass = null;
175
if (type instanceof SimpleType) {
176
final Class< ? > elemntClass = getClassOf(type, false);
177
klass = getArrayClassOf(elemntClass.getName(), 1, false);
179
else if (type instanceof PrimitiveType) {
180
klass = getPrimitiveArrayType(type.toString(), 1);
183
else if (type instanceof SimpleType) {
184
name = ((SimpleType) type).getName().getFullyQualifiedName();
186
else if (type instanceof PrimitiveType) {
187
//TODO: "char x[]" and not "char[] x" comes as Primitive type, this should be handled.
188
name = ((PrimitiveType) type).getPrimitiveTypeCode().toString();
189
klass = getPrimitiveType(name);
191
else if (type instanceof ArrayType) {
192
final ArrayType arrayType = (ArrayType) type;
193
final Type elementType = arrayType.getElementType();
194
if (elementType instanceof SimpleType) {
195
final Class< ? > elemntClass = getClassOf(elementType, false);
196
klass = getArrayClassOf(elemntClass.getName(), arrayType.getDimensions(), false);
198
else if (elementType instanceof PrimitiveType) {
199
klass = getPrimitiveArrayType(elementType.toString(), arrayType.getDimensions());
202
throw new IllegalStateException("Can not process type of " + type.getClass().getName());
205
else if (type instanceof ParameterizedType) {
206
klass = getClassOf(((ParameterizedType) type).getType(), isVararg);
209
throw new IllegalStateException("Can not process type of " + type.getClass().getName());
212
klass = tryClassName(name);
214
final CompilationUnit cu = (CompilationUnit) type.getRoot();
215
if (klass == null && cu.getPackage() != null) {
216
klass = tryClassName(cu.getPackage().getName().getFullyQualifiedName() + '.' + name);
219
for (final Object t : cu.types()) {
220
klass = searchClassInAllTypes((TypeDeclaration) t, name);
227
klass = tryClassName("java.lang." + name);
231
for (final Object i : cu.imports()) {
232
String imported = ((ImportDeclaration) i).toString().trim().substring(7);
233
imported = imported.substring(0, imported.length() - 1);
234
if (imported.endsWith('.' + name)) {
235
klass = tryClassName(imported);
239
final int period = imported.lastIndexOf('.');
240
while (period != -1) {
241
imported = imported.substring(0, period) + '$' + imported.substring(period + 1);
242
klass = tryClassName(imported);
248
else if (imported.endsWith(".*;")) {
249
//TODO: we need to check for any inner class (which needs '$' instead of '.')
250
klass = tryClassName(imported.substring(0, imported.length() - 1) + name);
258
throw new IllegalStateException("Can not get class of " + type);
263
private static Class< ? > tryClassName(final String className) {
265
return Class.forName(className);
267
catch (final Throwable t) {
272
private static Class< ? > searchClassInAllTypes(final TypeDeclaration typeDeclaration, final String name) {
273
Class< ? > klass = tryClassName(getFullyQualifiedName(typeDeclaration) + '$' + name);
277
for (final Object type : typeDeclaration.getTypes()) {
278
klass = searchClassInAllTypes((TypeDeclaration) type, name);
286
private static Class< ? > getPrimitiveArrayType(final String name, final int dimensions) {
287
final String className;
288
if (name.equals("byte")) {
291
else if (name.equals("short")) {
294
else if (name.equals("int")) {
297
else if (name.equals("long")) {
300
else if (name.equals("char")) {
303
else if (name.equals("float")) {
306
else if (name.equals("double")) {
309
else if (name.equals("boolean")) {
313
throw new IllegalArgumentException("Invalid primitive type " + name);
316
return getArrayClassOf(className, dimensions, true);
319
private static Class< ? > getArrayClassOf(final String className, final int dimensions, final boolean primitive) {
320
final StringBuilder builder = new StringBuilder();
321
for (int i = 0; i < dimensions; i++) {
327
builder.append(className);
331
return tryClassName(builder.toString());
334
private static Class< ? > getPrimitiveType(final String name) {
335
if (name.equals("byte")) {
338
if (name.equals("short")) {
341
if (name.equals("int")) {
344
if (name.equals("long")) {
347
if (name.equals("char")) {
350
if (name.equals("float")) {
353
if (name.equals("double")) {
356
if (name.equals("boolean")) {
357
return boolean.class;
359
if (name.equals("void")) {