code: ///////////////////////////////////////////////////////////////////////////////////////////////
  // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
  // Copyright (C) 2001-2023 the original author or authors.
  //
  // This library is free software; you can redistribute it and/or
  // modify it under the terms of the GNU Lesser General Public
  // License as published by the Free Software Foundation; either
  // version 2.1 of the License, or (at your option) any later version.
  //
  // This library is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  // Lesser General Public License for more details.
  //
  // You should have received a copy of the GNU Lesser General Public
  // License along with this library; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  ///////////////////////////////////////////////////////////////////////////////////////////////
  
  package com.puppycrawl.tools.checkstyle.checks.header;
  
  import java.io.BufferedInputStream;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.LineNumberReader;
  import java.io.Reader;
  import java.io.StringReader;
  import java.net.URI;
  import java.nio.charset.Charset;
  import java.nio.charset.StandardCharsets;
  import java.nio.charset.UnsupportedCharsetException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Set;
  import java.util.regex.Pattern;
  
  import com.puppycrawl.tools.checkstyle.PropertyType;
  import com.puppycrawl.tools.checkstyle.XdocsPropertyType;
  import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
  import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
  
  /**
   * Abstract super class for header checks.
   * Provides support for header and headerFile properties.
   */
  public abstract class AbstractHeaderCheck extends AbstractFileSetCheck
      implements ExternalResourceHolder {
  
      /** Pattern to detect occurrences of '\n' in text. */
      private static final Pattern ESCAPED_LINE_FEED_PATTERN = Pattern.compile("\\\\n");
  
      /** The lines of the header file. */
      private final List<String> readerLines = new ArrayList<>();
  
      /** Specify the name of the file containing the required header. */
      private URI headerFile;
  
      /** Specify the character encoding to use when reading the headerFile. */
      @XdocsPropertyType(PropertyType.STRING)
      private Charset charset = createCharset(System.getProperty("file.encoding",
          StandardCharsets.UTF_8.name()));
  
      /**
       * Hook method for post-processing header lines.
       * This implementation does nothing.
       */
      protected abstract void postProcessHeaderLines();
  
      /**
       * Return the header lines to check against.
       *
       * @return the header lines to check against.
       */
      protected List<String> getHeaderLines() {
          return List.copyOf(readerLines);
      }
  
      /**
       * Setter to specify the charset to use when reading the headerFile.
       *
       * @param charset the charset name to use for loading the header from a file
       */
      public void setCharset(String charset) {
          this.charset = createCharset(charset);
      }
  
      /**
       * Setter to specify the name of the file containing the required header..
       *
       * @param uri the uri of the header to load.
       * @throws CheckstyleException if fileName is empty.
       */
      public void setHeaderFile(URI uri) throws CheckstyleException {
          if (uri == null) {
              throw new CheckstyleException(
                  "property 'headerFile' is missing or invalid in module "
                      + getConfiguration().getName());
          }
  
          headerFile = uri;
      }
  
      /**
       * Load the header from a file.
       *
       * @throws CheckstyleException if the file cannot be loaded
       */
      private void loadHeaderFile() throws CheckstyleException {
          checkHeaderNotInitialized();
          try (Reader headerReader = new InputStreamReader(new BufferedInputStream(
                      headerFile.toURL().openStream()), charset)) {
              loadHeader(headerReader);
          }
          catch (final IOException ex) {
              throw new CheckstyleException(
                      "unable to load header file " + headerFile, ex);
          }
      }
  
      /**
       * Called before initializing the header.
       *
       * @throws IllegalArgumentException if header has already been set
       */
      private void checkHeaderNotInitialized() {
          if (!readerLines.isEmpty()) {
              throw new IllegalArgumentException(
                      "header has already been set - "
                      + "set either header or headerFile, not both");
          }
      }
  
      /**
       * Creates charset by name.
       *
       * @param name charset name
       * @return created charset
       * @throws UnsupportedCharsetException if charset is unsupported
       */
      private static Charset createCharset(String name) {
          if (!Charset.isSupported(name)) {
              final String message = "unsupported charset: '" + name + "'";
              throw new UnsupportedCharsetException(message);
          }
          return Charset.forName(name);
      }
  
      /**
       * Set the header to check against. Individual lines in the header
       * must be separated by '\n' characters.
       *
       * @param header header content to check against.
       * @throws IllegalArgumentException if the header cannot be interpreted
       */
      public void setHeader(String header) {
          if (!CommonUtil.isBlank(header)) {
              checkHeaderNotInitialized();
  
              final String headerExpandedNewLines = ESCAPED_LINE_FEED_PATTERN
                      .matcher(header).replaceAll("\n");
  
              try (Reader headerReader = new StringReader(headerExpandedNewLines)) {
                  loadHeader(headerReader);
              }
              catch (final IOException ex) {
                  throw new IllegalArgumentException("unable to load header", ex);
              }
          }
      }
  
      /**
       * Load header to check against from a Reader into readerLines.
       *
       * @param headerReader delivers the header to check against
       * @throws IOException if
       */
      private void loadHeader(final Reader headerReader) throws IOException {
          try (LineNumberReader lnr = new LineNumberReader(headerReader)) {
              String line;
              do {
                  line = lnr.readLine();
                  if (line != null) {
                      readerLines.add(line);
                  }
              } while (line != null);
              postProcessHeaderLines();
          }
      }
  
      @Override
      protected final void finishLocalSetup() throws CheckstyleException {
          if (headerFile != null) {
              loadHeaderFile();
          }
      }
  
      @Override
      public Set<String> getExternalResourceLocations() {
          final Set<String> result;
  
          if (headerFile == null) {
              result = Collections.emptySet();
          }
          else {
              result = Collections.singleton(headerFile.toString());
          }
  
          return result;
      }
  
  }
  