001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.javadoc;
021
022import java.io.File;
023import java.io.IOException;
024import java.util.Set;
025import java.util.concurrent.ConcurrentHashMap;
026
027import com.puppycrawl.tools.checkstyle.GlobalStatefulCheck;
028import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
029import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
030import com.puppycrawl.tools.checkstyle.api.FileText;
031
032/**
033 * <p>
034 * Checks that each Java package has a Javadoc file used for commenting.
035 * By default it only allows a {@code package-info.java} file,
036 * but can be configured to allow a {@code package.html} file.
037 * </p>
038 * <p>
039 * A violation will be reported if both files exist as this is not allowed by the Javadoc tool.
040 * </p>
041 * <ul>
042 * <li>
043 * Property {@code allowLegacy} - Allow legacy {@code package.html} file to be used.
044 * Default value is {@code false}.
045 * </li>
046 * <li>
047 * Property {@code fileExtensions} - Specify the file type extension of files to process.
048 * Default value is {@code .java}.
049 * </li>
050 * </ul>
051 * <p>
052 * To configure the check:
053 * </p>
054 * <pre>
055 * &lt;module name="JavadocPackage"/&gt;
056 * </pre>
057 * <p>
058 * To configure the check to use legacy {@code package.html} file
059 * when {@code package-info.java} file is absent:
060 * </p>
061 * <pre>
062 * &lt;module name="JavadocPackage"&gt;
063 *   &lt;property name="allowLegacy" value="true"/&gt;
064 * &lt;/module&gt;
065 * </pre>
066 *
067 * @since 5.0
068 */
069@GlobalStatefulCheck
070public class JavadocPackageCheck extends AbstractFileSetCheck {
071
072    /**
073     * A key is pointing to the warning message text in "messages.properties"
074     * file.
075     */
076    public static final String MSG_LEGACY_PACKAGE_HTML = "javadoc.legacyPackageHtml";
077
078    /**
079     * A key is pointing to the warning message text in "messages.properties"
080     * file.
081     */
082    public static final String MSG_PACKAGE_INFO = "javadoc.packageInfo";
083
084    /** The directories checked. */
085    private final Set<File> directoriesChecked = ConcurrentHashMap.newKeySet();
086
087    /** Allow legacy {@code package.html} file to be used. */
088    private boolean allowLegacy;
089
090    /**
091     * Creates a new instance.
092     */
093    public JavadocPackageCheck() {
094        // java, not html!
095        // The rule is: Every JAVA file should have a package.html sibling
096        setFileExtensions("java");
097    }
098
099    @Override
100    protected void processFiltered(File file, FileText fileText) throws CheckstyleException {
101        // Check if already processed directory
102        final File dir;
103        try {
104            dir = file.getCanonicalFile().getParentFile();
105        }
106        catch (IOException ex) {
107            throw new CheckstyleException(
108                    "Exception while getting canonical path to file " + file.getPath(), ex);
109        }
110        final boolean isDirChecked = !directoriesChecked.add(dir);
111        if (!isDirChecked) {
112            // Check for the preferred file.
113            final File packageInfo = new File(dir, "package-info.java");
114            final File packageHtml = new File(dir, "package.html");
115
116            if (packageInfo.exists()) {
117                if (packageHtml.exists()) {
118                    log(1, MSG_LEGACY_PACKAGE_HTML);
119                }
120            }
121            else if (!allowLegacy || !packageHtml.exists()) {
122                log(1, MSG_PACKAGE_INFO);
123            }
124        }
125    }
126
127    /**
128     * Setter to allow legacy {@code package.html} file to be used.
129     *
130     * @param allowLegacy whether to allow support.
131     */
132    public void setAllowLegacy(boolean allowLegacy) {
133        this.allowLegacy = allowLegacy;
134    }
135
136}