KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > tasklist > copyright > CopyrightChecker


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.tasklist.copyright;
21
22 import javax.swing.text.*;
23 import java.awt.*;
24 import java.text.SimpleDateFormat JavaDoc;
25 import javax.swing.*;
26 import java.util.List JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Date JavaDoc;
29 import java.util.regex.*;
30
31 import org.openide.ErrorManager;
32 import org.openide.loaders.DataObject;
33 import org.openide.loaders.DataObjectNotFoundException;
34 import org.openide.util.NbBundle;
35
36 import org.netbeans.modules.tasklist.client.*;
37 import org.netbeans.modules.tasklist.providers.DocumentSuggestionProvider;
38 import org.netbeans.modules.tasklist.providers.SuggestionContext;
39
40 import org.netbeans.modules.tasklist.core.TLUtils;
41
42 /**
43  * This class scans the given document errors in the copyright
44  * declaration.
45  *
46  * Copyright idea:
47  * search for n lines (or m characters) for the regexp
48  * copyright|Copyright|COPYRIGHT|(C)|(c)|©
49  * Then on any matching lines, look for year-tokens,
50  * where a year token is a 4 digit number beginning with 19 or 20.
51  * (y2.1k bug!) Three supported patterns:
52  * NNNN : single year.
53  * NNNN-MMMM : year range
54  * NNNN,MMMM,OOOO,...: year list
55  * For single year, see if NNNN < current year. If so,
56  * add task to change it to NNNN-currentyear.
57  * For year range, see if MMMM < current year. If so,
58  * add task to change it to NNNN-currentyear.
59  * For year list, see if currentyear is in the list. If not,
60  * add current year to the list (or better yet, change the range to
61  * lowest-highest).
62  *
63  * Optionally, also offer to add copyright to files missing one.
64  *
65  * @author Tor Norbye
66  * @author Tim Lebedkov
67  */

68 public class CopyrightChecker extends DocumentSuggestionProvider {
69     final private static String JavaDoc TYPE = "nb-tasklist-copyright"; // NOI18N
70

71     // Get current year
72
private final String JavaDoc year = new SimpleDateFormat JavaDoc("yyyy").format(new Date JavaDoc());
73     
74     /** The list of tasks we're currently showing in the tasklist */
75     private List JavaDoc showingTasks = null;
76     
77     private Pattern re;
78
79     public CopyrightChecker() {
80         try {
81             // XXX make this configurable?
82
re = Pattern.compile("copyright|Copyright|COPYRIGHT|\\(C\\)|\\(c\\)|\u00a9"); // NOI18N
83
} catch (PatternSyntaxException e) {
84             // Internal error: the regexp should have been validated when
85
// the user edited it
86
ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
87         }
88     }
89     
90     public String JavaDoc getType() {
91         return TYPE;
92     }
93
94     public List JavaDoc scan(SuggestionContext env) {
95         SuggestionManager manager = SuggestionManager.getDefault();
96         if (!manager.isEnabled(TYPE)) {
97             return null;
98         }
99         Suggestion s = checkCopyright(env);
100         if (s != null) {
101             List JavaDoc tasks = new ArrayList JavaDoc(1);
102             tasks.add(s);
103             return tasks;
104         }
105         return null;
106     }
107
108     /**
109      * Check the top of the document to see if there's a copyright
110      * in it, and if so, check that the year includes the current
111      * year. Support is limited to cases where
112      * <ul>
113      * <li> The copyright string and the year list appear on the same line
114      * <li> The years are 4 digits long, not 2 or some other number
115      * <li> The years are between 1900 and 2100 (this restriction can
116      * be removed and is there to avoid false positives such that
117      * only years are identified)
118      * <li> The copyright string is one of copyright, Copyright, COPYRIGHT,
119      * (C), (c), and ©.
120      * <li> The date is either a single years, a comma separated list of years,
121      * or a year range (separated with a dash, no whitespace surrounding
122      * the dash.)
123      * <li> There is only a single copyright declaration in the file
124      * (it will only consider the first one)
125      * <li> The copyright appears near the beginning of the file
126      * (currently in one of the first 10*80 characters although this
127      * may change)
128      * </ul>
129      *
130      * @param env a context
131      *
132      * @todo Decide if I really need both the updating and the replace
133      * flag and if updating doesn't perhaps imply replace
134      * @todo FIX PERFORMANCE! Really bad right now; creating a String from a
135      * document etc.
136      * @todo Perhaps merge it into the regular scanning (cl.line checking
137      * when line index < N) ?
138      * @todo Do year computation to make sure that if we have a FUTURE
139      * year in a date range, we can bail (ok, that won't work
140      * for copyright 2100-2150, but that seems absurd anyway)
141      * @todo Skip this check for read-only files? Can't edit them anyway
142      * @todo Only suggest this task once the document has been edited?
143      * If a user is -visiting- a file (e.g. during debugging) it
144      * may be pointless.
145      */

146     private Suggestion checkCopyright(final SuggestionContext env) {
147         // TODO - cache and share with other scanner!
148
CharSequence JavaDoc chars = env.getCharSequence();
149         int len = chars.length();
150         if (len > 10 * 80) {
151             len = 10 * 80; // Only look at the top 10 or so lines (truncate document)
152
}
153
154         int index = 0;
155         Matcher matcher = re.matcher(chars);
156         if (matcher.find(index)) { // TODO - find a way to bound the search?
157
int begin = matcher.start();
158             index = matcher.end();
159
160             // Compute the copyright line contents: back up to find the
161
// beginning of the line, then move forwards to skip the
162
// backspace. The line extends to the end of the line.
163

164             // Find end of line
165
while (index < len) {
166                 char c = chars.charAt(index);
167                 if (c == '\n' || c == '\r') {
168                     break;
169                 }
170                 index++;
171             }
172
173             // Find beginning of line
174
while (begin >= 0) {
175                 char c = chars.charAt(begin);
176                 if (c == '\n' || c == '\r') {
177                     break;
178                 }
179                 begin--;
180             }
181             begin++; // skip the \n
182
CharSequence JavaDoc line;
183             if (index < len) {
184                 line = chars.subSequence(begin, index);
185             } else {
186                 line = chars.subSequence(begin, chars.length());
187             }
188
189             // Scan copyright line to look for copyright years
190
int n = line.length() - 3; // only support 4-digit years for now
191
int c0 = -1; // previous character
192
int cp = -1; // previous previous character
193
int rangeEnd = -1;
194             int listEnd = -1;
195             int dateEnd = -1;
196             int firstDate = 0;
197             for (int i = 0; i < n; i++) {
198                 char c1 = line.charAt(i);
199                 char c2 = line.charAt(i + 1);
200                 char c3 = line.charAt(i + 2);
201                 char c4 = line.charAt(i + 3);
202                 if ((c1 == '1' || (c1 == '2')) && // Y3K bug here!
203
(c2 == '9' || (c2 == '0')) && // Y2.1K bug here!
204
Character.isDigit(c3) &&
205                         Character.isDigit(c4)) {
206
207                     if (firstDate == 0) {
208                         firstDate = i;
209                     }
210
211                     // TODO -- what if the current year is GREATER than
212
// the current year? (e.g. somebody did
213
// copyright 1975-2010 just to be on the safe side?)
214
// Check for that!
215

216                     // See if the current year is in the list
217
if ((c4 == year.charAt(3)) &&
218                             (c3 == year.charAt(2)) &&
219                             (c2 == year.charAt(1)) &&
220                             (c1 == year.charAt(0))) {
221                         // Yes, so bail
222
return null;
223                     }
224
225                     // I've found a year!
226
if (c0 == '-') {
227                         // This is the potential end of a date-range
228
rangeEnd = i;
229                     } else if ((c0 == ',') || ((cp == ',') && (c0 == ' '))) {
230                         // This is the potential end of a comma-list
231
listEnd = i;
232                     } else {
233                         dateEnd = i;
234                     }
235                 }
236                 cp = c0;
237                 c0 = c1;
238             }
239
240             if ((rangeEnd == -1) && (listEnd == -1) && (dateEnd == -1)) {
241                 // Copyright in the text, but no year found ... could offer
242
// to add one, but where? For now, keep quiet
243
return null;
244             }
245
246             // Ensure that we use the last date occurrence as our insert position
247
// Reverse order of checks used below
248
if (dateEnd > listEnd) {
249                 listEnd = -1;
250             }
251             if (listEnd > rangeEnd) {
252                 rangeEnd = -1;
253             }
254
255             int lastDate = 0;
256             if (rangeEnd != -1) {
257                 lastDate = rangeEnd + 4;
258             } else if (listEnd != -1) {
259                 lastDate = listEnd + 4;
260             } else if (dateEnd != -1) {
261                 lastDate = dateEnd + 4;
262             }
263
264             String JavaDoc range;
265             if (rangeEnd != -1) {
266                 range = line.subSequence(firstDate, rangeEnd).toString() + year;
267             } else if (listEnd != -1) {
268                 range = line.subSequence(firstDate, lastDate).toString() +
269                         ", " + year; // NOI18N
270
} else { // assert dateEnd != -1
271
range = line.subSequence(firstDate, lastDate).toString() +
272                         "-" + year; // NOI18N
273
}
274
275             final String JavaDoc oldRange = line.subSequence(firstDate, lastDate).toString();
276             final String JavaDoc newRange = range;
277             String JavaDoc description =
278                     NbBundle.getMessage(CopyrightChecker.class,
279                             "CopyrightDesc", // NOI18N
280
newRange, oldRange);
281
282             int linenum = 1;
283             for (int k = 0; k < begin; k++) {
284                 // TODO make sure this works on other platforms with
285
// strange newline handling (e.g. windows: \r\n, mac: \r)
286
// Currently it won't work if you're on a \r system
287
if (chars.charAt(k) == '\n') {
288                     linenum++;
289                 }
290             }
291             final int lineno = linenum;
292
293             SuggestionManager manager = SuggestionManager.getDefault();
294             SuggestionAgent copyrightTask = manager.createSuggestion(
295                 env.getFileObject(), TYPE, description, null, this);
296             try {
297                 DataObject dataObject = DataObject.find(env.getFileObject());
298                 copyrightTask.setLine(TLUtils.getLineByNumber(dataObject, lineno));
299             } catch (DataObjectNotFoundException e) {
300                 // ignore
301
ErrorManager.getDefault().notify(e);
302             }
303
304             //final String verify = "";
305
// XXX Not used. Idea was to
306
// produce string
307
// expected at line[pos] to make sure user hasn't messed with
308
// the line (aha! Let's just store the line!)
309

310             // XXX IMPORTANT! Invalidate the fixer as soon as the document
311
// is edited. Alternatively, "recompute" the positions before
312
// actually committing the change!
313
final int fRangeEnd = rangeEnd;
314             final int fBegin = begin;
315             final int fListEnd = listEnd;
316             final int fDateEnd = dateEnd;
317             final Document doc = env.getDocument();
318             
319             SuggestionPerformer jackson = new ChangeCopyrightDatesPerformer(
320                 env, fRangeEnd, fBegin, fListEnd, fDateEnd, doc, lineno, year);
321             copyrightTask.setAction(jackson);
322             copyrightTask.setPriority(SuggestionPriority.LOW);
323             return copyrightTask.getSuggestion();
324         } else {
325             // Make no-copyright warnings optional
326
// TODO - check for that here
327

328             String JavaDoc warning = NbBundle.getMessage(CopyrightChecker.class,
329                     "NoCopyright"); // NOI18N
330

331             SuggestionPerformer action = new AddCopyrightPerformer(env);
332             SuggestionManager manager = SuggestionManager.getDefault();
333             SuggestionAgent copyrightTask = manager.createSuggestion(
334                 env.getFileObject(), TYPE, warning, action, this);
335             try {
336                 DataObject dataObject = DataObject.find(env.getFileObject());
337                 copyrightTask.setLine(TLUtils.getLineByNumber(dataObject, 1));
338             } catch (DataObjectNotFoundException e) {
339                 // ignore
340
ErrorManager.getDefault().notify(e);
341             }
342             
343             // Normal priority sounds about right
344
return copyrightTask.getSuggestion();
345         }
346     }
347 }
348
Popular Tags