gt:             this.itemList = new LinkedList<>();
context: /*
   * FindBugs - Find bugs in Java programs
   * Copyright (C) 2003-2005 University of Maryland
   *
   * 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
   */
  
  /*
   * Project.java
   *
   * Created on March 30, 2003, 2:22 PM
   */
  
  package edu.umd.cs.findbugs;
  import static edu.umd.cs.findbugs.xml.XMLOutputUtil.writeElementList;
  import static java.util.Objects.requireNonNull;
  
  import java.io.BufferedInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.io.Reader;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.LinkedHashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.jar.Attributes;
  import java.util.jar.Manifest;
  
  import javax.annotation.CheckForNull;
  import javax.annotation.Nonnull;
  
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.XMLReader;
  import org.xml.sax.helpers.XMLReaderFactory;
  
  import edu.umd.cs.findbugs.ba.SourceFinder;
  import edu.umd.cs.findbugs.ba.URLClassPath;
  import edu.umd.cs.findbugs.charsets.UTF8;
  import edu.umd.cs.findbugs.config.UserPreferences;
  import edu.umd.cs.findbugs.filter.Filter;
  import edu.umd.cs.findbugs.util.Util;
  import edu.umd.cs.findbugs.xml.OutputStreamXMLOutput;
  import edu.umd.cs.findbugs.xml.XMLAttributeList;
  import edu.umd.cs.findbugs.xml.XMLOutput;
  import edu.umd.cs.findbugs.xml.XMLOutputUtil;
  import edu.umd.cs.findbugs.xml.XMLWriteable;
  
  /**
   * <p>A project in the GUI. This consists of some number of Jar files to analyze
   * for bugs, and optionally
   * </p>
   * <ul>
   * <li>some number of source directories, for locating the program's source code</li>
   * <li>some number of auxiliary classpath entries, for locating classes
   * referenced by the program which the user doesn't want to analyze</li>
   * <li>some number of boolean options</li>
   * </ul>
   *
   * @author David Hovemeyer
   */
  public class Project implements XMLWriteable {
      private static final boolean DEBUG = SystemProperties.getBoolean("findbugs.project.debug");
  
      private final List<File> currentWorkingDirectoryList;
  
      private String projectName;
  
      /**
       * List of jars/directories to analyze
       */
      private List<String> analysisTargets;
  
      /**
       * The list of source directories.
       */
      private List<String> srcDirList;
  
      /**
       * The list of auxiliary classpath entries.
       */
      private List<String> auxClasspathEntryList;
  
      /**
       * Flag to indicate that this Project has been modified.
       */
      private boolean isModified;
  
      private UserPreferences configuration;
  
      /** key is plugin id */
      private final Map<String,Boolean> enabledPlugins;
  
      @CheckForNull
      public Boolean getPluginStatus(Plugin plugin) {
          return enabledPlugins.get(plugin.getPluginId());
      }
  
      public void setPluginStatusTrinary(String pluginId, Boolean enabled) {
          enabledPlugins.put(pluginId, enabled);
      }
  
      public UserPreferences getConfiguration() {
          return configuration;
      }
  
      /**
       * @param configuration The configuration to set, non null
       */
      public void setConfiguration(@Nonnull UserPreferences configuration) {
          requireNonNull(configuration);
          this.configuration = configuration;
      }
  
      /**
       * StaticConstant used to name anonymous projects.
       */
      public static final String UNNAMED_PROJECT = "<<unnamed project>>";
  
      private long timestampForAnalyzedClasses = 0L;
  
      private IGuiCallback guiCallback;
  
      @Nonnull
      private Filter suppressionFilter = new Filter();
  
      private SourceFinder sourceFinder;
  
      /**
       * Create an anonymous project.
       */
      public Project() {
          enabledPlugins = new HashMap<>();
          configuration = UserPreferences.createDefaultUserPreferences();
          analysisTargets = new LinkedList<>();
          srcDirList = new LinkedList<>();
          auxClasspathEntryList = new LinkedList<>();
          isModified = false;
          currentWorkingDirectoryList = new ArrayList<>();
      }
  
      /**
       * Return an exact copy of this Project.
       */
      public Project duplicate() {
          Project dup = new Project();
          dup.currentWorkingDirectoryList.addAll(this.currentWorkingDirectoryList);
          dup.projectName = this.projectName;
          dup.analysisTargets.addAll(this.analysisTargets);
          dup.srcDirList.addAll(this.srcDirList);
          dup.auxClasspathEntryList.addAll(this.auxClasspathEntryList);
          dup.timestampForAnalyzedClasses = timestampForAnalyzedClasses;
          dup.guiCallback = guiCallback;
          return dup;
      }
  
      public SourceFinder getSourceFinder() {
          if (sourceFinder == null) {
              sourceFinder = new SourceFinder(this);
          }
          return sourceFinder;
      }
  
      public boolean isGuiAvaliable() {
          return guiCallback != null;
      }
  
      /**
       * add information from project2 to this project
       */
      public void add(Project project2) {
          analysisTargets = appendWithoutDuplicates(analysisTargets, project2.analysisTargets);
          srcDirList = appendWithoutDuplicates(srcDirList, project2.srcDirList);
          auxClasspathEntryList = appendWithoutDuplicates(auxClasspathEntryList, project2.auxClasspathEntryList);
      }
  
      public static <T> List<T> appendWithoutDuplicates(List<T> lst1, List<T> lst2) {
          LinkedHashSet<T> joined = new LinkedHashSet<>(lst1);
          joined.addAll(lst2);
          return new ArrayList<>(joined);
  
      }
  
      public void setCurrentWorkingDirectory(File f) {
          if (f != null) {
              addWorkingDir(f.toString());
          }
      }
  
      /**
       * Return whether or not this Project has unsaved modifications.
       */
      public boolean isModified() {
          return isModified;
      }
  
      /**
       * Set whether or not this Project has unsaved modifications.
       */
      public void setModified(boolean isModified) {
          this.isModified = isModified;
      }
  
      /**
       * Add a file to the project.
       *
       * @param fileName
       *            the file to add
       * @return true if the file was added, or false if the file was already
       *         present
       */
      public boolean addFile(String fileName) {
          return addToListInternal(analysisTargets, makeAbsoluteCWD(fileName));
      }
  
      /**
       * Add a source directory to the project.
       *
       * @param sourceDir
       *            The source directory to add. These can be either an absolute path
       *            or relative to any of the working directories in this project object.
       * @return true if the source directory was added or false if it was already present
       * @deprecated Calling this method repeatedly performs poorly because a SourceFinder is created each time,
       *              which checks all files for existence each time. Use {@link #addSourceDirs} instead.
       */
      @Deprecated
      public boolean addSourceDir(String sourceDir) {
          return addSourceDirs(Collections.singletonList(sourceDir));
      }
  
      /**
       * Add source directories to the project.
       *
       * @param sourceDirs
       *            The source directories to add. These can be either absolute paths
       *            or relative to any of the working directories in this project object.
       * @return true if a source directory was added or false if all source
       *         directories were already present
       */
      public boolean addSourceDirs(Collection<String> sourceDirs) {
          boolean isNew = false;
  
          if (sourceDirs == null || sourceDirs.isEmpty()) {
              return isNew;
          }
  
          for (String dirName : sourceDirs) {
              for (String dir : makeAbsoluteCwdCandidates(dirName)) {
                  isNew = addToListInternal(srcDirList, dir) || isNew;
              }
          }
  
          sourceFinder = new SourceFinder(this);
          return isNew;
      }
  
      /**
       * Add a working directory to the project.
       *
       * @param dirName
       *            the directory to add
       * @return true if the working directory was added, or false if the working
       *         directory was already present
       */
      public boolean addWorkingDir(String dirName) {
          if (dirName == null) {
              throw new NullPointerException();
          }
          return addToListInternal(currentWorkingDirectoryList, new File(dirName));
      }
  
      /**
       * Get the number of files in the project.
       *
       * @return the number of files in the project
       */
      public int getFileCount() {
          return analysisTargets.size();
      }
  
      /**
       * Get the given file in the list of project files.
       *
       * @param num
       *            the number of the file in the list of project files
       * @return the name of the file
       */
      public String getFile(int num) {
          return analysisTargets.get(num);
      }
  
      /**
       * Remove file at the given index in the list of project files
       *
       * @param num
       *            index of the file to remove in the list of project files
       */
      public void removeFile(int num) {
          analysisTargets.remove(num);
          isModified = true;
      }
  
      /**
       * Get the list of files, directories, and zip files in the project.
       */
      public List<String> getFileList() {
          return analysisTargets;
      }
  
      /**
       * Get the number of source directories in the project.
       *
       * @return the number of source directories in the project
       */
      public int getNumSourceDirs() {
          return srcDirList.size();
      }
  
      /**
       * Get the given source directory.
       *
       * @param num
       *            the number of the source directory
       * @return the source directory
       */
      public String getSourceDir(int num) {
          return srcDirList.get(num);
      }
  
      /**
       * Remove source directory at given index.
       *
       * @param num
       *            index of the source directory to remove
       */
      public void removeSourceDir(int num) {
          srcDirList.remove(num);
          sourceFinder = new SourceFinder(this);
          isModified = true;
      }
  
      /**
       * Get project files as an array of Strings.
       */
      public String[] getFileArray() {
          return analysisTargets.toArray(new String[analysisTargets.size()]);
      }
  
      /**
       * Get source dirs as an array of Strings.
       */
      public String[] getSourceDirArray() {
          return srcDirList.toArray(new String[srcDirList.size()]);
      }
  
      /**
       * Get the source dir list.
       */
      public List<String> getSourceDirList() {
          return srcDirList;
      }
  
      /**
       * Add an auxiliary classpath entry
       *
       * @param auxClasspathEntry
       *            the entry
       * @return true if the entry was added successfully, or false if the given
       *         entry is already in the list
       */
      public boolean addAuxClasspathEntry(String auxClasspathEntry) {
          return addToListInternal(auxClasspathEntryList, makeAbsoluteCWD(auxClasspathEntry));
      }
  
      /**
       * Get the number of auxiliary classpath entries.
       */
      public int getNumAuxClasspathEntries() {
          return auxClasspathEntryList.size();
      }
  
      /**
       * Get the n'th auxiliary classpath entry.
       */
      public String getAuxClasspathEntry(int n) {
          return auxClasspathEntryList.get(n);
      }
  
      /**
       * Remove the n'th auxiliary classpath entry.
       */
      public void removeAuxClasspathEntry(int n) {
          auxClasspathEntryList.remove(n);
          isModified = true;
      }
  
      /**
       * Return the list of aux classpath entries.
       */
      public List<String> getAuxClasspathEntryList() {
          return auxClasspathEntryList;
      }
  
      /**
       * Worklist item for finding implicit classpath entries.
       */
      private static class WorkListItem {
          private final URL url;
  
          /**
           * Constructor.
           *
           * @param url
           *            the URL of the Jar or Zip file
           */
          public WorkListItem(URL url) {
              this.url = url;
          }
  
          /**
           * Get URL of Jar/Zip file.
           */
          public URL getURL() {
              return this.url;
          }
      }
  
      /**
       * Worklist for finding implicit classpath entries.
       */
      private static class WorkList {
          private final LinkedList<WorkListItem> itemList;
  
          private final HashSet<String> addedSet;
  
          /**
           * Constructor. Creates an empty worklist.
           */
          public WorkList() {
  