001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.nbbuild;
043:
044: import java.io.*;
045: import java.nio.ByteBuffer;
046: import java.nio.CharBuffer;
047: import java.nio.channels.FileChannel;
048: import java.nio.charset.Charset;
049: import java.util.*;
050: import java.util.regex.*;
051:
052: import org.apache.tools.ant.*;
053: import org.apache.tools.ant.types.FileSet;
054:
055: /** Task to search some files for bad constructions.
056: * @author Jesse Glick
057: */
058: public class FindBadConstructions extends Task {
059:
060: private static final Pattern lineBreak = Pattern.compile("^",
061: Pattern.MULTILINE);
062:
063: private List<FileSet> filesets = new LinkedList<FileSet>();
064: private List<Construction> bad = new LinkedList<Construction>();
065:
066: /** Add a set of files to scan. */
067: public void addFileset(FileSet fs) {
068: filesets.add(fs);
069: }
070:
071: /** Add a construction that is bad. */
072: public Construction createConstruction() {
073: Construction c = new Construction();
074: bad.add(c);
075: return c;
076: }
077:
078: /** One bad construction. */
079: public class Construction {
080: Pattern regexp;
081: String message = null;
082: int show = -1;
083:
084: public Construction() {
085: }
086:
087: /**
088: * Set the bad regular expression to search for.
089: * Use embedded flags to set any desired pattern behaviors like case insensitivity;
090: * multiline mode is always on.
091: */
092: public void setRegexp(String r) throws BuildException {
093: try {
094: regexp = Pattern.compile(r, Pattern.MULTILINE);
095: } catch (PatternSyntaxException rese) {
096: throw new BuildException(rese, getLocation());
097: }
098: }
099:
100: /** Set an optional message to display as output. */
101: public void setMessage(String m) {
102: message = m;
103: }
104:
105: /** Set whether to display the matching text (by default no), and if so which part.
106: * @param s 0 means complete match; 1 or higher means that-numbered parenthesis
107: */
108: public void setShowMatch(int s) {
109: show = s;
110: }
111: }
112:
113: public void execute() throws BuildException {
114: if (filesets.isEmpty())
115: throw new BuildException("Must give at least one fileset",
116: getLocation());
117: if (bad.isEmpty())
118: throw new BuildException(
119: "Must give at least one construction",
120: getLocation());
121: for (FileSet fs : filesets) {
122: FileScanner scanner = fs.getDirectoryScanner(getProject());
123: File dir = scanner.getBasedir();
124: String[] files = scanner.getIncludedFiles();
125: log("Scanning " + files.length + " files in " + dir);
126: for (String name : files) {
127: File f = new File(dir, name);
128: //System.err.println("working on " + f);
129: try {
130: for (Construction c : bad) {
131: if (c.regexp == null)
132: throw new BuildException(
133: "Must specify regexp on a construction",
134: getLocation());
135: FileInputStream fis = new FileInputStream(f);
136: FileChannel fc = fis.getChannel();
137: try {
138: ByteBuffer bb = fc.map(
139: FileChannel.MapMode.READ_ONLY, 0L,
140: fc.size());
141: Charset cs = Charset.forName("UTF-8");
142: CharBuffer content = cs.decode(bb);
143: Matcher m = c.regexp.matcher(content);
144: while (m.find()) {
145: StringBuffer message = new StringBuffer(
146: 1000);
147: message.append(f.getAbsolutePath());
148: message.append(':');
149: Matcher lbm = lineBreak
150: .matcher(content);
151: int line = 0;
152: int col = 1;
153: while (lbm.find()) {
154: if (lbm.start() <= m.start()) {
155: line++;
156: col = m.start() - lbm.start()
157: + 1;
158: } else {
159: break;
160: }
161: }
162: message.append(line);
163: message.append(":");
164: message.append(col);
165: message.append(": ");
166: if (c.message != null) {
167: message.append(c.message);
168: }
169: if (c.show != -1) {
170: if (c.message != null) {
171: message.append(": ");
172: }
173: message.append(m.group(c.show));
174: }
175: if (c.show == -1 && c.message == null) {
176: message
177: .append("bad construction found");
178: }
179: log(message.toString(),
180: Project.MSG_WARN);
181: }
182: } finally {
183: fc.close();
184: fis.close();
185: }
186: }
187: } catch (IOException ioe) {
188: throw new BuildException("Error reading " + f, ioe,
189: getLocation());
190: }
191: }
192: }
193: }
194:
195: }
|