# RootNodeList.java

    /*
    ** Module   : RootNodeList.java
    ** Abstract : maintains a project-specific list of root entry point procedures
    **
    ** Copyright (c) 2005-2018, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- -----------------------------Description-----------------------------
    ** 001 GES 20050314   @20312 Created initial version which has support
    **                           for a persistable XML root node list.
    ** 002 ECF 20050317   @20418 Constructor now throws ConfigurationException
    **                           if root node list file parameter is not found
    **                           in configuration.
    ** 003 ECF 20050421   @20809 Import p2j.xml package to use XmlHelper.
    ** 004 GES 20090429   @42063 Match package and class name changes.
    ** 005 GES 20090515   @42231 Import change.
    ** 006 CA  20140313          Added possibility to specify the files in a folder as entry-points.
    ** 007 ECF 20160407          Added TODO in c'tor to check for rootlist hint.
    ** 008 GES 20170531          Fix loading when current directory is not the same as project home.
    ** 009 CA  20170922          Fixed a path compatibility when generating the callgraph on windows.
    ** 010 CA  20181128          Fixed rootlist file calculation when folders are used.
    */
    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program 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 Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.uast;

    import java.io.*;
    import java.util.*;
    import org.w3c.dom.*;
    import com.goldencode.ast.*;
    import com.goldencode.util.*;
    import com.goldencode.p2j.cfg.*;
    import com.goldencode.p2j.util.*;

    /**
     * Reads a project-specific list of root entry point procedures.
     * <p>
     * The list is flat with a root element of 'roots' and all 'node' elements
     * enclosed within that root element. Each node element has a single
     * 'filename' attribute with the root node file name.  No nesting of nodes
     * is provided or supported.
     */
    public class RootNodeList
    {
       /** Key for the global root node list filename configuration parameter. */
       private static final String ROOTFILE_KEY = "rootlist";

       /** Root element containing the list. */
       private static final String ELEM_ROOT  = "roots";

       /** Represents a single root entry point. */
       private static final String ELEM_NODE  = "node";

       /** The filename that is associated with a root node. */
       private static final String ATTR_FILE  = "filename";

       /** The folder name that is associated with a node. */
       private static final String ATTR_FOLDER  = "folder";

       /** Attribute flag indicating if a folder will be searched recursively. */
       private static final String ATTR_RECURSIVE = "recursive";

       /** The pattern used to list files from a folder. */
       private static final String ATTR_PATTERN  = "pattern";

       /** XML filename for persisting the registry. */
       private File file = null;

       /** Stores the root node list. */
       private ArrayList<String> nodes = null;

       /**
        * Constructor which looks up the root node list filename and initializes
        * its data structures based on reading that file, if it exists.  The
        * filename is stored in the global configuration.
        *
        * @throws  ConfigurationException
        *          if the root node list file is not configured properly.
        * @throws  AstException
        *          if the XML parser has a failure during parsing or in its
        *          file system operations.
        * @throws  FileNotFoundException
        *          if a root file or folder does not exist.
        */
       public RootNodeList()
       throws ConfigurationException,
              AstException,
              FileNotFoundException
       {
          String rootNodePath = Configuration.getParameter(ROOTFILE_KEY);

          if (rootNodePath == null)
          {
             // TODO: check UAST hints

             throw new ConfigurationException(
                "Root node list file is not configured properly");
          }

          file = new File(Configuration.forceHome(rootNodePath));

          // try to load the persisted root node list
          if (file.exists())
             load();
       }

       /**
        * Accesses the list of root nodes as read from the master XML file.
        *
        * @return    The root node list of filenames or <code>null</code> if
        *            there are no entries in the list.
        */
       public String[] getList()
       {
          if (nodes == null)
             return null;

          return (String[])nodes.toArray(new String[0]);
       }

       /**
        * Read the source XML document and build the list from the stored
        * elements.
        *
        * @throws  AstException
        *          if the XML parser has a failure during parsing or in its
        *          file system operations.
        * @throws  IllegalArgumentException
        *          if the input file is invalid or doesn't exist.
        * @throws  FileNotFoundException
        *          if a root file or folder does not exist.
        */
       private void load()
       throws AstException,
              FileNotFoundException
       {
          Document dom  = null;
          Element  root = null;

          try
          {
             if (file.isFile())
             {
                dom  = XmlHelper.parse(file);
                root = dom.getDocumentElement();
             }
             else
             {
                throw new AstException("Bogus root node list " +
                                        file.toString());
             }
          }
          catch (Exception excpt)
          {
             throw new AstException(
                "Error loading root node XML file: " + file.toString(), excpt);
          }

          NodeList list = root.getElementsByTagName(ELEM_NODE);

          int len = list.getLength();

          // leave our list as null, if there are no entries
          if (len > 0)
          {
             nodes = new ArrayList<>();
          }

          for (int i = 0; i < len; i++)
          {
             Element elem = (Element) list.item(i);
             String  name = elem.getAttribute(ATTR_FILE);
             if (name == null || name.trim().length() == 0)
             {
                name = elem.getAttribute(ATTR_FOLDER);
                File folder = new File(Configuration.forceHome(name));
                if (folder == null || !folder.exists())
                {
                   throw new FileNotFoundException("Folder '" + name + "' does not exist!");
                }

                String pattern = elem.getAttribute(ATTR_PATTERN);
                if (pattern == null || pattern.trim().length() == 0)
                {
                   pattern = "*.ast";
                }

                boolean recursive = "true".equalsIgnoreCase(elem.getAttribute(ATTR_RECURSIVE));
                FileSpecList spec = new FileSpecList(folder, pattern, recursive);

                for (String filename : spec.listFilenames())
                {
                   filename = Configuration.normalizeFilename(filename);

                   nodes.add(filename);
                }
             }
             else
             {
                File file = new File(Configuration.forceHome(name));
                if (!file.exists())
                {
                   throw new FileNotFoundException("File '" + name + "' does not exist!");
                }
                file = new File(name);
                nodes.add(file.getPath());
             }
          }
       }
    }