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());
         }
      }
   }
}