001: /*
002: * Hammurapi
003: * Automated Java code review system.
004: * Copyright (C) 2004 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.org
021: * e-Mail: support@hammurapi.biz
022: */
023: package org.hammurapi.inspectors;
024:
025: import java.util.Collection;
026: import java.util.HashSet;
027: import java.util.Iterator;
028:
029: import org.hammurapi.InspectorBase;
030: import org.hammurapi.HammurapiException;
031: import org.hammurapi.InspectorContext;
032:
033: import com.pavelvlasov.jsel.Declaration;
034: import com.pavelvlasov.jsel.VariableDefinition;
035: import com.pavelvlasov.jsel.expressions.AssignmentExpression;
036: import com.pavelvlasov.jsel.expressions.ConditionalExpression;
037: import com.pavelvlasov.jsel.expressions.Expression;
038: import com.pavelvlasov.jsel.expressions.ExpressionList;
039: import com.pavelvlasov.jsel.expressions.Ident;
040: import com.pavelvlasov.jsel.expressions.LogicalOr;
041: import com.pavelvlasov.jsel.statements.ForStatement;
042: import com.pavelvlasov.review.SourceMarker;
043: import com.pavelvlasov.util.AccumulatingVisitorExceptionSink;
044: import com.pavelvlasov.util.DispatchingVisitor;
045: import com.pavelvlasov.util.Visitable;
046:
047: /**
048: * ER-098
049: * Do not assign loop control variables in the body of a "for" loop
050: * @author Janos Czako
051: * @author Pavel Vlasov
052: * @version $Revision: 1.5 $
053: */
054: public class ForLoopControlVariablesRule extends InspectorBase {
055:
056: /**
057: * The chained visitor class which searches after assigning
058: * the loop control variable.
059: */
060: public static class Snooper {
061:
062: /**
063: * The name of the loop control variable, searched for.
064: */
065: private Collection names;
066: private InspectorContext context;
067:
068: /**
069: * Constructor, takes the name searched for.
070: *
071: * @param theIdentName the name of the variable searched for.
072: */
073: public Snooper(final Collection names, InspectorContext context) {
074: this .names = names;
075: this .context = context;
076: }
077:
078: /**
079: * Reviews the assignments.
080: *
081: * @param element the assignment to be reviewed.
082: */
083: public void visit(AssignmentExpression element) {
084: if (!(element instanceof LogicalOr || element instanceof ConditionalExpression)) {
085: if (element.getOperand(0) instanceof Ident) {
086: Ident ident = (Ident) element.getOperand(0);
087: if (names.contains(ident.getText())) {
088: context.reportViolation((SourceMarker) element);
089: }
090: }
091: }
092: }
093: }
094:
095: /**
096: * The error text for exceptions in the chained visitor.
097: */
098: private static final String CHAINED_ERRS = "There have been exceptions (see above)";
099:
100: /**
101: * Reviews the for statement if it violates agains the rule
102: *
103: * @param element the fro statement
104: * @throws HammurapiException in case of any exception in the chained visitor
105: */
106: public void visit(ForStatement element) throws HammurapiException {
107: Collection names = new HashSet();
108: if (element.getInitializer() != null) {
109: if (element.getInitializer() instanceof Declaration) {
110: getNames((Declaration) element.getInitializer(), names);
111: } else if (element.getInitializer() instanceof ExpressionList) {
112: getNames((ExpressionList) element.getInitializer(),
113: names);
114: }
115: }
116:
117: if (!names.isEmpty()) {
118: Snooper rs = new Snooper(names, context);
119: // TODO Don't use DispatchingVisitor, use plain visitor
120: // TODO Use Scope.getVariableNamespace().find() to make sure that
121: // first operand of assignment expression is control variable
122: AccumulatingVisitorExceptionSink es = new AccumulatingVisitorExceptionSink();
123: ((Visitable) element.getStatement())
124: .accept(new DispatchingVisitor(rs, es));
125:
126: if (!es.getExceptions().isEmpty()) {
127: es.dump();
128: throw new HammurapiException(CHAINED_ERRS);
129: }
130: }
131: }
132:
133: /**
134: * Gets the control variable name in for loop
135: * if it is declared in the for statement
136: *
137: * @param element the declaration
138: * @return the name of the variable
139: */
140: private void getNames(Declaration element, Collection names) {
141: Iterator it = element.iterator();
142: while (it.hasNext()) {
143: VariableDefinition vd = (VariableDefinition) it.next();
144: names.add(vd.getName());
145: }
146: }
147:
148: /**
149: * Gets the control variable name in for loop
150: * if it is declared not in the for statement
151: *
152: * @param element the assigment expression from the for statement
153: * @return the assignment expression
154: */
155: private void getNames(ExpressionList element, Collection names) {
156: Iterator it = element.iterator();
157: while (it.hasNext()) {
158: Object o = it.next();
159: if (o instanceof AssignmentExpression
160: && !(o instanceof LogicalOr || o instanceof ConditionalExpression)
161: && ((Expression) o).getOperand(0) instanceof Ident) {
162: names.add(((Ident) ((Expression) o).getOperand(0))
163: .getText());
164: }
165: }
166: }
167: }
|