KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hammurapi > inspectors > IndentationRule


1 /*
2   * Hammurapi
3  * Automated Java code review system.
4  * Copyright (C) 2004 Hammurapi Group
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * URL: http://www.hammurapi.org
21  * e-Mail: support@hammurapi.biz
22  */

23 package org.hammurapi.inspectors;
24
25 import java.io.File JavaDoc;
26
27 import org.hammurapi.InspectorBase;
28
29 import com.pavelvlasov.config.ConfigurationException;
30 import com.pavelvlasov.config.Parameterizable;
31 import com.pavelvlasov.jsel.Class;
32 import com.pavelvlasov.jsel.CompilationUnit;
33 import com.pavelvlasov.jsel.Interface;
34 import com.pavelvlasov.jsel.LanguageElement;
35 import com.pavelvlasov.jsel.Package;
36 import com.pavelvlasov.jsel.impl.JavaTokenTypes;
37 import com.pavelvlasov.jsel.impl.Token;
38 import com.pavelvlasov.review.SimpleSourceMarker;
39
40 /**
41  * <p>
42  * Hammurapi inspector for checking the indentation of the source code.
43  * </p>
44  * <p>
45  * The parameter <i>standard-indentation-level </i> specifies the default
46  * indentation for blocks in curly braces. There must be no difference in the
47  * indentation of statements without sorrounding curly braces. Examples (with
48  * <i>standard-indentation-level </i>= 2):
49  * </p>
50  *
51  * <pre>
52  * if (current.getType() == JavaTokenTypes.LCURLY) {
53  * requiredColumn += indentLevel;
54  * }
55  *
56  * while (parent != null &amp;&amp; parent instanceof Class) {
57  * lassLevel++;
58  * parent = parent.getParent();
59  * }
60  * </pre>
61  *
62  * <p>
63  * If large expressions (e.g. in if-Statements) are longer than a single line it
64  * is recommended to use a different indentation. This is useful for
65  * distinguishing these expressions from the following Java blocks. In the same
66  * way the indentation of long assignment values, parameter, lists, long chains
67  * of method calls and throws-statement should be handled. This different
68  * indentation is specified by the parameter <i>expression-indentation-level
69  * </i>. Examples (with <i>expression-indentation-level </i>= 4):
70  * </p>
71  *
72  * <pre>
73  * if (current.getType() == JavaTokenTypes.LPAREN &amp;&amp; parenthesisLevel++ == 0) {
74  * requiredColumn += exprIndentLevel;
75  * }
76  *
77  * check((Token) aClass.getAst().getFirstToken(), getClassLevel(aClass),
78  * (Token) aClass.getAst().getLastToken());
79  * </pre>
80  *
81  * @author Jochen Skulj
82  * @version $Revision: 1.4 $
83  */

84 public class IndentationRule extends InspectorBase implements Parameterizable {
85
86   /**
87    * parameter name for the standard indentation
88    */

89   public static final String JavaDoc PARAMETER_INDENT = "standard-indentation-level";
90
91   /**
92    * parameter name for the expression indentation
93    */

94   public static final String JavaDoc PARAMETER_EXPR_INDENT = "expression-indentation-level";
95
96   /**
97    * standard indentation
98    */

99   private int indentLevel;
100
101   /**
102    * expression indentation
103    */

104   private int exprIndentLevel;
105
106   /**
107    * source marker for reporting violating tokens
108    */

109   private SimpleSourceMarker sourceMarker;
110
111   /**
112    * constructor
113    */

114   public IndentationRule() {
115     indentLevel = 2;
116     exprIndentLevel = 4;
117   }
118
119   /**
120    * checks indentation of tokens of a class
121    *
122    * @param aClass
123    * class to inspect
124    */

125   public void visit(Class JavaDoc aClass) {
126     initSourceMarker(aClass);
127     check((Token) aClass.getAst().getFirstToken(), getClassLevel(aClass),
128         (Token) aClass.getAst().getLastToken());
129   }
130
131   /**
132    * checks indentation of tokens of an interface
133    *
134    * @param anInterface
135    * interface to inspect
136    */

137   public void visit(Interface anInterface) {
138     check((Token) anInterface.getAst().getFirstToken(),
139         getClassLevel(anInterface), (Token) anInterface.getAst().getLastToken());
140   }
141
142   /**
143    * initializes the source marker for the current language element
144    *
145    * @param anElement
146    * current language element
147    */

148   protected void initSourceMarker(LanguageElement anElement) {
149     sourceMarker = new SimpleSourceMarker(anElement);
150     CompilationUnit unit = anElement.getCompilationUnit();
151     Package JavaDoc pack = unit.getPackage();
152     if (pack.getName().length() == 0) {
153       sourceMarker.setSourceURL(unit.getName());
154     } else {
155       sourceMarker.setSourceURL(pack.getName().replace('.', File.separatorChar)
156           + File.separator + unit.getName());
157     }
158   }
159
160   /**
161    * counts the parent classes
162    *
163    * @param anElement
164    * current class or interface
165    * @return number of parent classes
166    */

167   protected int getClassLevel(LanguageElement anElement) {
168     int classLevel = 0;
169     LanguageElement parent = anElement.getParent();
170     while (parent != null && parent instanceof Class JavaDoc) {
171       classLevel++;
172       parent = parent.getParent();
173     }
174     return classLevel;
175   }
176
177   /**
178    * checks the indentation of a set of tokens
179    *
180    * @param firstToken
181    * first token to inspect
182    * @param classLevel
183    * number of surrounding classes (if current language element is an
184    * inner class or interface
185    * @param lastToken
186    * last token to inspect
187    */

188   protected void check(Token firstToken, int classLevel, Token lastToken) {
189     Token current = firstToken;
190     if (current != null) {
191       int line = current.getLine();
192       int requiredColumn = 1 + (classLevel * indentLevel);
193       int parenthesisLevel = 0;
194       boolean assignment = false;
195       do {
196         // check, if a RCURLY affects the required indentation
197
if (current.getType() == JavaTokenTypes.RCURLY) {
198           requiredColumn -= indentLevel;
199         }
200         // check, if the begin of an assignment affects the possible indentation
201
if (current.getType() == JavaTokenTypes.ASSIGN && !assignment) {
202           assignment = true;
203           requiredColumn += exprIndentLevel;
204         }
205         // check, if the begin of an assignment affects the possible indentation
206
if (current.getType() == JavaTokenTypes.SEMI && assignment) {
207           assignment = false;
208           requiredColumn -= exprIndentLevel;
209         }
210         // if a new line is reached check indentation
211
if (current.getLine() > line) {
212           // continue with standard indentation checks if the current line
213
// does not begin with a throws literal or a method call outside of an
214
// assignment
215
if (current.getType() != JavaTokenTypes.LITERAL_throws
216               && current.getType() != JavaTokenTypes.DOT) {
217             if (current.getColumn() != requiredColumn) {
218               sourceMarker.setLine(current.getLine());
219               sourceMarker.setColumn(current.getColumn());
220               context.reportViolation(sourceMarker);
221             }
222           } else {
223             // special check for throws literals and a method calls
224
int specialRequiredColumn = assignment ? requiredColumn
225                 : requiredColumn + exprIndentLevel;
226             if (current.getColumn() != specialRequiredColumn) {
227               sourceMarker.setLine(current.getLine());
228               sourceMarker.setColumn(current.getColumn());
229               context.reportViolation(sourceMarker);
230             }
231           }
232           line = current.getLine();
233         }
234         // check, if a RCURLY affects the required indentation
235
if (current.getType() == JavaTokenTypes.LCURLY) {
236           requiredColumn += indentLevel;
237         }
238         // check, if an expressions in parenthesises affects the required
239
// indentation
240
if (current.getType() == JavaTokenTypes.LPAREN
241             && parenthesisLevel++ == 0) {
242           requiredColumn += exprIndentLevel;
243         }
244         if (current.getType() == JavaTokenTypes.RPAREN
245             && --parenthesisLevel == 0) {
246           requiredColumn -= exprIndentLevel;
247         }
248         current = current.getNextNonWhiteSpaceToken();
249       } while (current != null && current != lastToken);
250     }
251   }
252
253   /**
254    * sets parameters for the inspector
255    *
256    * @param aName
257    * parameter name
258    * @param aParameter
259    * parameter value
260    */

261   public boolean setParameter(String JavaDoc aName, Object JavaDoc aValue)
262       throws ConfigurationException {
263     boolean parameterSupported = false;
264     if (aName.equals(PARAMETER_INDENT)) {
265       parameterSupported = true;
266       indentLevel = ((Integer JavaDoc) aValue).intValue();
267     }
268     if (aName.equals(PARAMETER_EXPR_INDENT)) {
269       parameterSupported = true;
270       exprIndentLevel = ((Integer JavaDoc) aValue).intValue();
271     }
272     if (!parameterSupported) {
273       throw new ConfigurationException("Parameter '" + aName
274           + "' is not supported by " + getClass().getName());
275     }
276     return true;
277   }
278 }
Popular Tags