From d1a297476448ea9feec80af04416f198dc462bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20K=C3=BChne?= Date: Sat, 16 Oct 2004 17:13:04 +0000 Subject: [PATCH] rfe #1041590, allow header specification directly in checkstyle config file --- docs/config_header.html | 56 +++++++++- docs/releasenotes.html | 18 ++++ .../checks/AbstractHeaderCheck.java | 101 ++++++++++++++++-- .../checkstyle/checks/RegexpHeaderCheck.java | 25 ++++- .../checkstyle/checks/HeaderCheckTest.java | 29 ++++- 5 files changed, 212 insertions(+), 17 deletions(-) diff --git a/docs/config_header.html b/docs/config_header.html index 3ef89759f..5453916ff 100644 --- a/docs/config_header.html +++ b/docs/config_header.html @@ -37,6 +37,8 @@

Checks that a source file begins with a specified header. Property headerFile specifies a file that contains the required header. + Alternatively, the header specification can be set directly in the + header property without the need for an external file.

Property ignoreLines specifies the line numbers to @@ -70,6 +72,15 @@ line 5: //////////////////////////////////////////////////////////////////// string null + + header + the required header specified inline. Individual header lines + must be separated by the string "\n" + (even on platforms with a different line separator), + see examples below. + string + null + ignoreLines line numbers to ignore @@ -90,6 +101,23 @@ line 5: //////////////////////////////////////////////////////////////////// <property name="ignoreLines" value="2, 3, 4"/> </module> + +

+ To configure the check to verify that each file starts with the header +

+
+// Copyright (C) 2004 MyCompany
+// All rights reserved
+      
+

+ without the need for an external header file: +

+
+<module name="Header">
+    <property name="header" value="// Copyright (C) 2004 MyCompany\n// All rights reserved"/>
+</module>
+      
+

Package

com.puppycrawl.tools.checkstyle.checks @@ -100,7 +128,7 @@ line 5: ////////////////////////////////////////////////////////////////////

RegexpHeader

Description

- Checks the header of a source file against a header file that contains a regular + Checks the header of a source file against a header that contains a regular expression for each line of the source header.

@@ -109,7 +137,7 @@ line 5: //////////////////////////////////////////////////////////////////// information is not static.

- For example, consider the following header file: + For example, consider the following header:

 line  1: ^/{71}$
@@ -150,6 +178,15 @@ line 14: ^ \*/
           string
           null
         
+        
+          header
+          the required header specified inline. Individual header lines
+          must be separated by the string "\n"
+          (even on platforms with a different line separator), and regular
+          expressions must not span multiple lines.
+          string
+          null
+        
         
           multiLines
           line numbers to repeat (zero or more times)
@@ -169,6 +206,21 @@ line 14: ^ \*/
 <module name="RegexpHeader">
     <property name="headerFile" value="java.header"/>
     <property name="multiLines" value="10, 13"/>
+</module>
+      
+

+ To configure the check to verify that each file starts with the header +

+
+^// Copyright \(C\) (\d\d\d\d -)? 2004 MyCompany$
+^// All rights reserved$
+      
+

+ without the need for an external header file: +

+
+<module name="RegexpHeader">
+    <property name="header" value="^// Copyright \(C\) (\d\d\d\d -)? 2004 MyCompany$\n^// All rights reserved$"/>
 </module>
       

diff --git a/docs/releasenotes.html b/docs/releasenotes.html index f40876a49..bffc187e1 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -35,6 +35,7 @@

Checkstyle 3

+

+ New features: +

+ + + +

Release 3.5

+

New features:

@@ -125,6 +139,10 @@ checkstyle-user). and abstract methods. (request 993922 and bug 1002849). +
  • Header and RegexpHeader checks allow header + specification directly in the checkstyle configuration file, + not only in an external file (request 1041590). +
  • com.puppycrawl.tools.checkstyle.gui.Main accepts an optional file name in the command line. (request 1000102).
  • diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/AbstractHeaderCheck.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/AbstractHeaderCheck.java index 99b9a347b..ef7afed1a 100644 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/AbstractHeaderCheck.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/AbstractHeaderCheck.java @@ -22,12 +22,16 @@ package com.puppycrawl.tools.checkstyle.checks; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; +import java.io.Reader; +import java.io.StringReader; import java.util.ArrayList; import com.puppycrawl.tools.checkstyle.api.Check; import com.puppycrawl.tools.checkstyle.api.CheckstyleException; +import com.puppycrawl.tools.checkstyle.api.Utils; import org.apache.commons.beanutils.ConversionException; +import org.apache.regexp.RE; /** * Abstract super class for header checks. @@ -61,25 +65,100 @@ public abstract class AbstractHeaderCheck extends Check return; } + checkHeaderNotInitialized(); + // load the file + Reader headerReader = null; try { - final LineNumberReader lnr = - new LineNumberReader(new FileReader(aFileName)); - final ArrayList lines = new ArrayList(); - while (true) { - final String l = lnr.readLine(); - if (l == null) { - break; - } - lines.add(l); - } - mHeaderLines = (String[]) lines.toArray(new String[0]); + headerReader = new FileReader(aFileName); + loadHeader(headerReader); } catch (IOException ex) { throw new ConversionException( "unable to load header file " + aFileName, ex); } + finally { + if (headerReader != null) { + try { + headerReader.close(); + } + catch (IOException ex) { + throw new ConversionException( + "unable to close header file " + aFileName, ex); + } + } + } + } + /** + * Set the header to check against. Individual lines in the header + * must be separated by '\n' characters. + * @param aHeader header content to check against. + * @throws ConversionException if the header cannot be interpreted + */ + public void setHeader(String aHeader) + { + if ((aHeader == null) || (aHeader.trim().length() == 0)) { + return; + } + + checkHeaderNotInitialized(); + + // in JDK 1.4 we'd simply do aHeader.replaceAll("\\\\n", "\n"); + final RE re = Utils.getRE("\\\\n"); + final String headerExpandedNewLines = re.subst(aHeader, "\n"); + + final Reader headerReader = new StringReader(headerExpandedNewLines); + try { + loadHeader(headerReader); + } + catch (IOException ex) { + throw new ConversionException( + "unable to load header", ex); + } + finally { + try { + headerReader.close(); + } + catch (IOException ex) { + // shouldn't happen with StringReader + throw new ConversionException( + "unable to close header", ex); + } + } + + } + + /** + * Called before initializing the header. + * @throws ConversionException if header has already been set + */ + private void checkHeaderNotInitialized() + { + if (mHeaderLines != null) { + throw new ConversionException( + "header has already been set - " + + "set either header or headerFile, not both"); + } + } + + /** + * Load header to check against from a Reader into mHeaderLines. + * @param aHeaderReader delivers the header to check against + * @throws IOException if + */ + private void loadHeader(final Reader aHeaderReader) throws IOException + { + final LineNumberReader lnr = new LineNumberReader(aHeaderReader); + final ArrayList lines = new ArrayList(); + while (true) { + final String l = lnr.readLine(); + if (l == null) { + break; + } + lines.add(l); + } + mHeaderLines = (String[]) lines.toArray(new String[0]); } /** diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/RegexpHeaderCheck.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/RegexpHeaderCheck.java index 762a0a48f..0ebc15e09 100644 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/RegexpHeaderCheck.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/RegexpHeaderCheck.java @@ -76,8 +76,8 @@ import com.puppycrawl.tools.checkstyle.api.Utils; * <property name="multiLines" value="10, 13"/> *</module> * - *

    Note: ignoreLines property was removed you should use ^.*$ regexp - * for line to ignore it. + *

    Note: ignoreLines property has been removed from this check to + * simplify it. The regular expression "^.*$" can be used to ignore a line. *

    * * @author Lars Kühne @@ -129,6 +129,25 @@ public class RegexpHeaderCheck extends AbstractHeaderCheck throws ConversionException { super.setHeaderFile(aFileName); + initHeaderRegexps(); + } + + /** + * Set the header to check against. Individual lines in the header + * must be separated by '\n' characters. + * @param aHeader header content to check against. + * @throws ConversionException if the header cannot be loaded or one line + * is not a regexp. + */ + public void setHeader(String aHeader) + { + super.setHeader(aHeader); + initHeaderRegexps(); + } + + /** Initializes {@link #mHeaderRegexps} from {@link #mHeaderLines}. */ + private void initHeaderRegexps() + { final String[] headerLines = getHeaderLines(); if (headerLines != null) { mHeaderRegexps = new RE[headerLines.length]; @@ -139,7 +158,7 @@ public class RegexpHeaderCheck extends AbstractHeaderCheck } catch (RESyntaxException ex) { throw new ConversionException( - "line " + i + " in header file is not a regexp"); + "line " + i + " in header specification is not a regular expression"); } } } diff --git a/src/tests/com/puppycrawl/tools/checkstyle/checks/HeaderCheckTest.java b/src/tests/com/puppycrawl/tools/checkstyle/checks/HeaderCheckTest.java index e3e14567e..a1322db70 100644 --- a/src/tests/com/puppycrawl/tools/checkstyle/checks/HeaderCheckTest.java +++ b/src/tests/com/puppycrawl/tools/checkstyle/checks/HeaderCheckTest.java @@ -30,6 +30,33 @@ public class HeaderCheckTest extends BaseCheckTestCase verify(checkConfig, getPath("InputScopeAnonInner.java"), expected); } + public void testInlineRegexpHeader() + throws Exception + { + final DefaultConfiguration checkConfig = + createCheckConfig(RegexpHeaderCheck.class); + checkConfig.addAttribute("header", "^/*$\\n// .*\\n// Created: 2002\\n^//.*\\n^//.*"); + final String[] expected = { + "3: Line does not match expected header line of '// Created: 2002'." + }; + verify(checkConfig, getPath("InputScopeAnonInner.java"), expected); + } + + public void testFailureForMultilineRegexp() + throws Exception + { + final DefaultConfiguration checkConfig = + createCheckConfig(RegexpHeaderCheck.class); + checkConfig.addAttribute("header", "^(.*\\n.*)"); + try { + createChecker(checkConfig); + fail("Checker creation should not succeed when regexp spans multiple lines"); + } + catch (CheckstyleException ex) { + // expected exception + } + } + public void testRegexpHeaderIgnore() throws Exception { final DefaultConfiguration checkConfig = @@ -122,7 +149,7 @@ public class HeaderCheckTest extends BaseCheckTestCase } } - public void testIllegalArgs() + public void testNonExistingHeaderFile() throws Exception { final DefaultConfiguration checkConfig =