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.filters;
024:
025: import java.util.Iterator;
026: import java.util.LinkedList;
027: import java.util.Stack;
028:
029: import org.apache.oro.text.GlobCompiler;
030: import org.apache.oro.text.regex.MalformedPatternException;
031: import org.apache.oro.text.regex.Pattern;
032: import org.apache.oro.text.regex.Perl5Matcher;
033: import org.hammurapi.InspectorBase;
034: import org.hammurapi.FilteringInspector;
035:
036: import com.pavelvlasov.config.ConfigurationException;
037: import com.pavelvlasov.config.Parameterizable;
038: import com.pavelvlasov.jsel.CompilationUnit;
039: import com.pavelvlasov.jsel.JselException;
040: import com.pavelvlasov.jsel.Operation;
041: import com.pavelvlasov.jsel.Package;
042: import com.pavelvlasov.jsel.TypeBody;
043: import com.pavelvlasov.jsel.TypeDefinition;
044:
045: /**
046: * Initial behavior of the filter to approve everything.
047: * @author Pavel Vlasov
048: * @version $Revision: 1.6 $
049: */
050: public class LanguageElementFilter extends InspectorBase implements
051: FilteringInspector, Parameterizable {
052:
053: private Stack approvals = new Stack();
054:
055: {
056: //Initial state of filter is approve
057: approvals.push(Boolean.TRUE);
058: }
059:
060: private static class ElementEntry {
061: boolean exclude;
062: String name;
063: String operationSignature;
064: Pattern fileNamePattern;
065: }
066:
067: private LinkedList elements = new LinkedList();
068:
069: public Boolean approve(Object element) {
070: return (Boolean) approvals.peek();
071: }
072:
073: private Perl5Matcher matcher = new Perl5Matcher();
074:
075: public void visit(CompilationUnit compilationUnit) {
076: Package pkg = compilationUnit.getPackage();
077: // Carry over previous state.
078: boolean decision = ((Boolean) approvals.peek()).booleanValue();
079:
080: Iterator it = elements.iterator();
081: while (it.hasNext()) {
082: ElementEntry entry = (ElementEntry) it.next();
083: if (entry.name != null
084: && entry.operationSignature == null
085: && ("*".equals(entry.name)
086: || pkg.getName().equals(entry.name) || (entry.name
087: .endsWith(".*") && pkg.getName()
088: .startsWith(
089: entry.name.substring(0, entry.name
090: .length() - 2))))) {
091: decision = !entry.exclude;
092: break;
093: }
094: }
095: approvals.push(decision ? Boolean.TRUE : Boolean.FALSE);
096:
097: // Carry over previous state.
098: decision = ((Boolean) approvals.peek()).booleanValue();
099:
100: it = elements.iterator();
101: while (it.hasNext()) {
102: ElementEntry entry = (ElementEntry) it.next();
103: if (entry.fileNamePattern != null
104: && matcher.matches(compilationUnit.getName(),
105: entry.fileNamePattern)) {
106: decision = !entry.exclude;
107: break;
108: }
109: }
110: approvals.push(decision ? Boolean.TRUE : Boolean.FALSE);
111: }
112:
113: public void leave(CompilationUnit compilaitonUnit) {
114: approvals.pop();
115: approvals.pop();
116: }
117:
118: /**
119: * Includes/excludes type only if package is included.
120: * @param typeBody
121: */
122: public void visit(TypeBody typeBody) {
123: // Carry over previous state.
124: boolean decision = ((Boolean) approvals.peek()).booleanValue();
125:
126: // Evaluate only if package included
127: if (decision) {
128: Iterator it = elements.iterator();
129: while (it.hasNext()) {
130: ElementEntry entry = (ElementEntry) it.next();
131: try {
132: if (entry.name != null
133: && entry.operationSignature == null
134: && typeBody.isKindOf(entry.name)) {
135: decision = !entry.exclude;
136: break;
137: }
138: } catch (JselException e) {
139: if (!(e.getCause() instanceof ClassNotFoundException)) {
140: context.warn(typeBody, e);
141: break;
142: }
143: }
144: }
145: }
146:
147: approvals.push(decision ? Boolean.TRUE : Boolean.FALSE);
148: }
149:
150: public void leave(TypeDefinition typeDefinition) {
151: approvals.pop();
152: }
153:
154: public void visit(Operation operation) {
155: // Carry over previous state.
156: boolean decision = ((Boolean) approvals.peek()).booleanValue();
157:
158: // Evaluate only if type included
159: if (decision) {
160: Iterator it = elements.iterator();
161: while (it.hasNext()) {
162: ElementEntry entry = (ElementEntry) it.next();
163: try {
164: if (entry.name != null
165: && entry.operationSignature != null
166: && ("*".equals(entry.name) || operation
167: .getEnclosingType().isKindOf(
168: entry.name))
169: && operation.getInfo()
170: .isArgumentCompatible(
171: entry.operationSignature)) {
172: decision = !entry.exclude;
173: break;
174: }
175: } catch (JselException e) {
176: context.warn(operation, e);
177: break;
178: }
179: }
180: }
181:
182: approvals.push(decision ? Boolean.TRUE : Boolean.FALSE);
183: }
184:
185: public void leave(Operation operation) {
186: approvals.pop();
187: }
188:
189: /**
190: * Supports <CODE>include</CODE>, <CODE>exclude</CODE>, <code>include-file</code>, and <code>exclude-file</code>.
191: * parameters. The first two apply to package, type and operation. The last two apply to compilation units.
192: * Order is significant - includes/excludes are applied
193: * in the same order as they defined. So including org.hammurapi.* and then excluding org.hammurapi.inspectors.* makes sense, but
194: * not vice versa.<BR>
195: * If package excluded all types and operations in it are excluded. If type is excluded all
196: * operations in this type are excluded.
197: * Operation parameters can contain <CODE>?</CODE> which matches any type.
198: * <P/><init> shall be used as operation name for constructors.<P/>
199: * <CODE>include</CODE> and <CODE>exclude</CODE> parameters format:
200: * <UL>
201: * <LI><CODE>*</code> - matches anything</LI>
202: * <LI><code><string></code> - matches package, type or operation (if contains '(')</LI>
203: * <LI><code><string>.*</code> - matches package and its subpackages</LI>
204: * </UL>
205: * Packages are matched literally, types are matched based on isKindOf(), including operation types as well.
206: * If operation doesn't contain type name then any type will match.<BR/>
207: *
208: * <code>include-file</code> and <code>exclude-file</code> parameter values are file name patterns. ? states for zero or one
209: * character, * states for zero or more characters. See {@link org.apache.oro.text.GlobCompiler} for more details.<p/>
210: * <DL>
211: * <DT><B>Examples</B></DT>
212: * <DD><code><parameter name="include">org.hammurapi</parameter></code> - includes package org.hammurapi</DD>
213: * <DD><code><parameter name="exclude">org.hammurapi.*</parameter></code> - excludes package org.hammurapi and its subpackages</DD>
214: * <DD><code><parameter name="exclude">org.hammurapi.InspectorDescriptor</parameter></code> - excludes interface org.hammurapi.InspectorDescriptor and its implementations</DD>
215: * <DD><code><parameter name="include">org.hammurapi.InspectorDescriptor.getMessage(java.lang.String)</parameter></code> -
216: * includes method interface org.hammurapi.InspectorDescriptor.getMessage(java.lang.String) and its implementations</DD>
217: * <DD><code><parameter name="exclude">toString()</parameter></code> - excludes toString() in all classes/interfaces</DD>
218: * <DD><code><parameter name="exclude">org.somepackage.SomeClass.<init>(?, java.util.List)</parameter></code> -
219: * excludes constructors which take two parameters, the first one of any type and the
220: * second one of type <code>java.util.List</code> or its implementations in <code>org.somepackage.SomeClass</code> and its subclasses</DD>
221: * <DD><code><parameter name="include-file">*.bpf</parameter></code> - includes .bpf files</DD>
222: */
223: public boolean setParameter(String name, Object parameter)
224: throws ConfigurationException {
225: if ("include".equals(name)) {
226: ElementEntry entry = new ElementEntry();
227: entry.exclude = false;
228: parseParameter(parameter, entry);
229: elements.addFirst(entry);
230: } else if ("exclude".equals(name)) {
231: ElementEntry entry = new ElementEntry();
232: entry.exclude = true;
233: parseParameter(parameter, entry);
234: elements.addFirst(entry);
235: } else if ("include-file".equals(name)) {
236: GlobCompiler compiler = new GlobCompiler();
237: ElementEntry entry = new ElementEntry();
238: entry.exclude = false;
239: try {
240: entry.fileNamePattern = compiler
241: .compile((String) parameter);
242: } catch (MalformedPatternException e) {
243: throw new ConfigurationException(e);
244: }
245: elements.addFirst(entry);
246: } else if ("exclude-file".equals(name)) {
247: GlobCompiler compiler = new GlobCompiler();
248: ElementEntry entry = new ElementEntry();
249: entry.exclude = true;
250: try {
251: entry.fileNamePattern = compiler
252: .compile((String) parameter);
253: } catch (MalformedPatternException e) {
254: throw new ConfigurationException(e);
255: }
256: elements.addFirst(entry);
257: } else {
258: throw new ConfigurationException(
259: "Not supported parameter: " + name);
260: }
261: return true;
262: }
263:
264: /**
265: * @param parameter
266: * @param entry
267: */
268: private static void parseParameter(Object parameter,
269: ElementEntry entry) {
270: String value = (String) parameter;
271: int pi = value.indexOf('(');
272: if (pi == -1) {
273: entry.name = value;
274: } else {
275: int lastDot = value.lastIndexOf('.', pi);
276: if (lastDot == -1) {
277: entry.name = "*";
278: entry.operationSignature = value;
279: } else {
280: entry.name = value.substring(0, lastDot);
281: entry.operationSignature = value.substring(lastDot + 1);
282: }
283: }
284: }
285: }
|