alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.tasklist.copyright;

import javax.swing.text.*;
import java.awt.*;
import java.text.SimpleDateFormat;
import javax.swing.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.util.regex.*;

import org.openide.ErrorManager;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.NbBundle;

import org.netbeans.modules.tasklist.client.*;
import org.netbeans.modules.tasklist.providers.DocumentSuggestionProvider;
import org.netbeans.modules.tasklist.providers.SuggestionContext;

import org.netbeans.modules.tasklist.core.TLUtils;

/**
 * This class scans the given document errors in the copyright
 * declaration.
 *
 * Copyright idea:
 * search for n lines (or m characters) for the regexp
 * copyright|Copyright|COPYRIGHT|(C)|(c)|\u00a9
 * Then on any matching lines, look for year-tokens,
 * where a year token is a 4 digit number beginning with 19 or 20.
 * (y2.1k bug!)    Three supported patterns:
 * NNNN      : single year.
 * NNNN-MMMM : year range
 * NNNN,MMMM,OOOO,...: year list
 * For single year, see if NNNN < current year. If so,
 * add task to change it to NNNN-currentyear.
 * For year range, see if MMMM < current year. If so,
 * add task to change it to NNNN-currentyear.
 * For year list, see if currentyear is in the list. If not,
 * add current year to the list (or better yet, change the range to
 * lowest-highest).
 *
 * Optionally, also offer to add copyright to files missing one.
 *
 * @author Tor Norbye
 * @author Tim Lebedkov
 */
public class CopyrightChecker extends DocumentSuggestionProvider {
    final private static String TYPE = "nb-tasklist-copyright"; // NOI18N
    
    // Get current year
    private final String year = new SimpleDateFormat("yyyy").format(new Date());
    
    /** The list of tasks we're currently showing in the tasklist */
    private List showingTasks = null;
    
    private Pattern re;

    public CopyrightChecker() {
        try {
            // XXX make this configurable?
            re = Pattern.compile("copyright|Copyright|COPYRIGHT|\\(C\\)|\\(c\\)|\u00a9"); // NOI18N
        } catch (PatternSyntaxException e) {
            // Internal error: the regexp should have been validated when
            // the user edited it
            ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
        }
    }
    
    public String getType() {
        return TYPE;
    }

    public List scan(SuggestionContext env) {
        SuggestionManager manager = SuggestionManager.getDefault();
        if (!manager.isEnabled(TYPE)) {
            return null;
        }
        Suggestion s = checkCopyright(env);
        if (s != null) {
            List tasks = new ArrayList(1);
            tasks.add(s);
            return tasks;
        }
        return null;
    }

    /**
     * Check the top of the document to see if there's a copyright
     * in it, and if so, check that the year includes the current
     * year.  Support is limited to cases where
     * 
    *
  • The copyright string and the year list appear on the same line *
  • The years are 4 digits long, not 2 or some other number *
  • The years are between 1900 and 2100 (this restriction can * be removed and is there to avoid false positives such that * only years are identified) *
  • The copyright string is one of copyright, Copyright, COPYRIGHT, * (C), (c), and \u00a9. *
  • The date is either a single years, a comma separated list of years, * or a year range (separated with a dash, no whitespace surrounding * the dash.) *
  • There is only a single copyright declaration in the file * (it will only consider the first one) *
  • The copyright appears near the beginning of the file * (currently in one of the first 10*80 characters although this * may change) *
* * @param env a context * * @todo Decide if I really need both the updating and the replace * flag and if updating doesn't perhaps imply replace * @todo FIX PERFORMANCE! Really bad right now; creating a String from a * document etc. * @todo Perhaps merge it into the regular scanning (cl.line checking * when line index < N) ? * @todo Do year computation to make sure that if we have a FUTURE * year in a date range, we can bail (ok, that won't work * for copyright 2100-2150, but that seems absurd anyway) * @todo Skip this check for read-only files? Can't edit them anyway * @todo Only suggest this task once the document has been edited? * If a user is -visiting- a file (e.g. during debugging) it * may be pointless. */ private Suggestion checkCopyright(final SuggestionContext env) { // TODO - cache and share with other scanner! CharSequence chars = env.getCharSequence(); int len = chars.length(); if (len > 10 * 80) { len = 10 * 80; // Only look at the top 10 or so lines (truncate document) } int index = 0; Matcher matcher = re.matcher(chars); if (matcher.find(index)) { // TODO - find a way to bound the search? int begin = matcher.start(); index = matcher.end(); // Compute the copyright line contents: back up to find the // beginning of the line, then move forwards to skip the // backspace. The line extends to the end of the line. // Find end of line while (index < len) { char c = chars.charAt(index); if (c == '\n' || c == '\r') { break; } index++; } // Find beginning of line while (begin >= 0) { char c = chars.charAt(begin); if (c == '\n' || c == '\r') { break; } begin--; } begin++; // skip the \n CharSequence line; if (index < len) { line = chars.subSequence(begin, index); } else { line = chars.subSequence(begin, chars.length()); } // Scan copyright line to look for copyright years int n = line.length() - 3; // only support 4-digit years for now int c0 = -1; // previous character int cp = -1; // previous previous character int rangeEnd = -1; int listEnd = -1; int dateEnd = -1; int firstDate = 0; for (int i = 0; i < n; i++) { char c1 = line.charAt(i); char c2 = line.charAt(i + 1); char c3 = line.charAt(i + 2); char c4 = line.charAt(i + 3); if ((c1 == '1' || (c1 == '2')) && // Y3K bug here! (c2 == '9' || (c2 == '0')) && // Y2.1K bug here! Character.isDigit(c3) && Character.isDigit(c4)) { if (firstDate == 0) { firstDate = i; } // TODO -- what if the current year is GREATER than // the current year? (e.g. somebody did // copyright 1975-2010 just to be on the safe side?) // Check for that! // See if the current year is in the list if ((c4 == year.charAt(3)) && (c3 == year.charAt(2)) && (c2 == year.charAt(1)) && (c1 == year.charAt(0))) { // Yes, so bail return null; } // I've found a year! if (c0 == '-') { // This is the potential end of a date-range rangeEnd = i; } else if ((c0 == ',') || ((cp == ',') && (c0 == ' '))) { // This is the potential end of a comma-list listEnd = i; } else { dateEnd = i; } } cp = c0; c0 = c1; } if ((rangeEnd == -1) && (listEnd == -1) && (dateEnd == -1)) { // Copyright in the text, but no year found ... could offer // to add one, but where? For now, keep quiet return null; } // Ensure that we use the last date occurrence as our insert position // Reverse order of checks used below if (dateEnd > listEnd) { listEnd = -1; } if (listEnd > rangeEnd) { rangeEnd = -1; } int lastDate = 0; if (rangeEnd != -1) { lastDate = rangeEnd + 4; } else if (listEnd != -1) { lastDate = listEnd + 4; } else if (dateEnd != -1) { lastDate = dateEnd + 4; } String range; if (rangeEnd != -1) { range = line.subSequence(firstDate, rangeEnd).toString() + year; } else if (listEnd != -1) { range = line.subSequence(firstDate, lastDate).toString() + ", " + year; // NOI18N } else { // assert dateEnd != -1 range = line.subSequence(firstDate, lastDate).toString() + "-" + year; // NOI18N } final String oldRange = line.subSequence(firstDate, lastDate).toString(); final String newRange = range; String description = NbBundle.getMessage(CopyrightChecker.class, "CopyrightDesc", // NOI18N newRange, oldRange); int linenum = 1; for (int k = 0; k < begin; k++) { // TODO make sure this works on other platforms with // strange newline handling (e.g. windows: \r\n, mac: \r) // Currently it won't work if you're on a \r system if (chars.charAt(k) == '\n') { linenum++; } } final int lineno = linenum; SuggestionManager manager = SuggestionManager.getDefault(); SuggestionAgent copyrightTask = manager.createSuggestion(TYPE, description, null, this); try { DataObject dataObject = DataObject.find(env.getFileObject()); copyrightTask.setLine(TLUtils.getLineByNumber(dataObject, lineno)); } catch (DataObjectNotFoundException e) { // ignore ErrorManager.getDefault().notify(e); } //final String verify = ""; // XXX Not used. Idea was to // produce string // expected at line[pos] to make sure user hasn't messed with // the line (aha! Let's just store the line!) // XXX IMPORTANT! Invalidate the fixer as soon as the document // is edited. Alternatively, "recompute" the positions before // actually committing the change! final int fRangeEnd = rangeEnd; final int fBegin = begin; final int fListEnd = listEnd; final int fDateEnd = dateEnd; final Document doc = env.getDocument(); SuggestionPerformer jackson = new ChangeCopyrightDatesPerformer( env, fRangeEnd, fBegin, fListEnd, fDateEnd, doc, lineno, year); copyrightTask.setAction(jackson); copyrightTask.setPriority(SuggestionPriority.LOW); return copyrightTask.getSuggestion(); } else { // Make no-copyright warnings optional // TODO - check for that here String warning = NbBundle.getMessage(CopyrightChecker.class, "NoCopyright"); // NOI18N SuggestionPerformer action = new AddCopyrightPerformer(env); SuggestionManager manager = SuggestionManager.getDefault(); SuggestionAgent copyrightTask = manager.createSuggestion(TYPE, warning, action, this); try { DataObject dataObject = DataObject.find(env.getFileObject()); copyrightTask.setLine(TLUtils.getLineByNumber(dataObject, 1)); } catch (DataObjectNotFoundException e) { // ignore ErrorManager.getDefault().notify(e); } // Normal priority sounds about right return copyrightTask.getSuggestion(); } } }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.