001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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.nio.file.Files;
025import java.nio.file.Path;
026import java.util.HashSet;
027import java.util.Set;
028
029import com.puppycrawl.tools.checkstyle.GlobalStatefulCheck;
030import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
031import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
032import com.puppycrawl.tools.checkstyle.api.FileText;
033
034/**
035 * <div>
036 * Checks that each Java package has a Javadoc file used for commenting.
037 * By default, it only allows a {@code package-info.java} file,
038 * but can be configured to allow a {@code package.html} file.
039 * </div>
040 *
041 * <p>
042 * A violation will be reported if both files exist as this is not allowed by the Javadoc tool.
043 * </p>
044 *
045 * @since 5.0
046 */
047@GlobalStatefulCheck
048public class JavadocPackageCheck extends AbstractFileSetCheck {
049
050    /**
051     * A key is pointing to the warning message text in "messages.properties"
052     * file.
053     */
054    public static final String MSG_LEGACY_PACKAGE_HTML = "javadoc.legacyPackageHtml";
055
056    /**
057     * A key is pointing to the warning message text in "messages.properties"
058     * file.
059     */
060    public static final String MSG_PACKAGE_INFO = "javadoc.packageInfo";
061
062    /** The directories checked. */
063    private final Set<File> directoriesChecked = new HashSet<>();
064
065    /** Allow legacy {@code package.html} file to be used. */
066    private boolean allowLegacy;
067
068    /**
069     * Creates a new instance.
070     */
071    public JavadocPackageCheck() {
072        // java, not html!
073        // The rule is: Every JAVA file should have a package.html sibling
074        setFileExtensions("java");
075    }
076
077    /**
078     * Setter to specify the file extensions of the files to process.
079     *
080     * @param extensions the set of file extensions. A missing
081     *         initial '.' character of an extension is automatically added.
082     * @throws IllegalArgumentException is argument is null
083     */
084    @Override
085    public final void setFileExtensions(String... extensions) {
086        super.setFileExtensions(extensions);
087    }
088
089    @Override
090    protected void processFiltered(File file, FileText fileText) throws CheckstyleException {
091        // Check if already processed directory
092        final File dir;
093        try {
094            dir = file.getCanonicalFile().getParentFile();
095        }
096        catch (IOException exc) {
097            throw new CheckstyleException(
098                    "Exception while getting canonical path to file " + file.getPath(), exc);
099        }
100        final boolean isDirChecked = !directoriesChecked.add(dir);
101        if (!isDirChecked) {
102            // Check for the preferred file.
103            final Path packageInfo = Path.of(dir.getPath(), "package-info.java");
104            final Path packageHtml = Path.of(dir.getPath(), "package.html");
105
106            if (Files.exists(packageInfo)) {
107                if (Files.exists(packageHtml)) {
108                    log(1, MSG_LEGACY_PACKAGE_HTML);
109                }
110            }
111            else if (!allowLegacy || !Files.exists(packageHtml)) {
112                log(1, MSG_PACKAGE_INFO);
113            }
114        }
115    }
116
117    /**
118     * Setter to allow legacy {@code package.html} file to be used.
119     *
120     * @param allowLegacy whether to allow support.
121     * @since 5.0
122     */
123    public void setAllowLegacy(boolean allowLegacy) {
124        this.allowLegacy = allowLegacy;
125    }
126
127}