Hints.java

/*
** Module   : Hints.java
** Abstract : Encapsulates elementary hints and corresponding XML tree.
**
** Copyright (c) 2004-2020, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- -----------------------------Description-----------------------------
** 001 NVS 20050513   @21162 Created. Initial implementation.
** 002 NVS 20050526   @21293 Cleaned the code up.
** 003 NVS 20050527   @21318 Separated XML tag into HintsTags interface.
** 004 NVS 20051003   @22928 Hints XML no longer contains creation date & time to produce 
**                           identical files for identical input. The hints file is not created at
**                           all unless there is at least one hints record.
** 005 GES 20090515   @42217 Import changes.
** 006 CA  20170825          Enhanced to allow collection and reporting of preprocessor constant
**                           symbols.
** 007 CA  20201015          Replaced java.util.Stack with ArrayDeque (as synchronization is not required).
*/
/*
** 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.preproc;

import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;

import com.goldencode.util.*;
import com.goldencode.p2j.*;

/**
 * Keeps information about elementary hints and organizes them into the tree.
 * <p>
 * @author NVS
 * @version 1.0.0
 */
public class Hints
implements HintsTags
{
   /** output XML file name */
   private String filename = null;

   /** ordered list of hints */
   private List list = null;

   /** stack of include hints */
   private Deque includes = null;

   /** XML document */
   private Document dom = null;

   /** stack of DOM parents */
   private Deque parents = null;

   /** count of essential elements */
   private int elementsCount = 0;

   /**
    * Constructor.
    *
    * @param filename
    *        hints file name or <code>null</code>
    * @throws  ParserConfigurationException
    *          Forwarded from XmlHelper.
    * @throws  IOException
    *          Forwarded from XmlHelper.
    */
   public Hints(String filename)
   throws ParserConfigurationException,
          IOException
   {
      list = new LinkedList();
      includes = new ArrayDeque();
      parents  = new ArrayDeque();

      this.filename = filename;
      if (filename != null)
      {
         dom = XmlHelper.newDocument();
         Comment comment = dom.createComment("Preprocessor hints");
         dom.appendChild(comment);

         Element root = dom.createElement(ELEM_TAG_ROOT);
         dom.appendChild(root);

         Element elem = dom.createElement(ELEM_TAG_SUBROOT);
         root.appendChild(elem);

         parents.push(elem);
      }
   }

   /**
    * Returns the hints list.
    *
    * @return
    *         the hints list
    */
   public List getHintsList()
   {
      return list;
   }

   /**
    * Returns the hints stack.
    *
    * @return
    *         the hints stack
    */
   public Deque getHintsStack()
   {
      return includes;
   }

   /**
    * Creates the include hint element.
    * Creation of this hint is done under the current parent element and then
    * this element is pushed onto the parents stack to become the current
    * parent.
    *
    * @param ih
    *        <code>IncludeHint</code> object to get the details from.
    */
   public void createAndPushIncludeHint(IncludeHint ih)
   {
      Element parent = (Element)parents.peek();

      Element elem = dom.createElement(ELEM_TAG_INCL);
      elementsCount ++;
      parent.appendChild(elem);
      
      XmlHelper.setAttribute(elem, ATTR_TAG_FILE, ih.getFilename());

      elem.setAttribute(ATTR_TAG_START_LINE, Integer.toString(
                                                          ih.getStartLine()));
      elem.setAttribute(ATTR_TAG_START_COL, Integer.toString(
                                                        ih.getStartColumn()));
      parents.push(elem);
   }

   /**
    * Completes the include hint element.
    * This element is popped off the parents stack.
    *
    * @param ih
    *        <code>IncludeHint</code> object to get the details from.
    */
   public void popAndCompleteIncludeHint(IncludeHint ih)
   {
      Element elem = (Element)parents.pop();

      elem.setAttribute(ATTR_TAG_END_LINE, Integer.toString(
                                                          ih.getEndLine()));
      elem.setAttribute(ATTR_TAG_END_COL, Integer.toString(
                                                          ih.getEndColumn()));
   }

   /**
    * Creates the argument hint element.
    * Creation of this hint is done under the current parent element.
    *
    * @param ah
    *        <code>ArgumentHint</code> object to get the details from.
    */
   public void createArgumentHint(ArgumentHint ah)
   {
      Element parent = (Element)parents.peek();

      Element elem = dom.createElement(ELEM_TAG_ARG);
      elementsCount ++;
      parent.appendChild(elem);
      
      String name = ah.getName();
      if (name.startsWith("{"))
         name = new String(name + "}");
      XmlHelper.setAttribute(elem, ATTR_TAG_ARG_NAME, name);

      elem.setAttribute(ATTR_TAG_ARG_POS, Integer.toString(ah.getPosition()));
      XmlHelper.setAttribute(elem, ATTR_TAG_ARG_VAL, ah.getValue());

      elem.setAttribute(ATTR_TAG_ARG_USED, ah.getUsed() ? "yes" : "no");
   }

   /**
    * Creates the reference hint element.
    * Creation of this hint is done under the current parent element.
    *
    * @param rh
    *        <code>ReferenceHint</code> object to get the details from.
    */
   public void createReferenceHint(ReferenceHint rh)
   {
      Element parent = (Element)parents.peek();

      Element elem = dom.createElement(ELEM_TAG_REF);
      elementsCount ++;
      parent.appendChild(elem);
      
      String name = rh.getName();
      if (name.startsWith("{"))
         name = new String(name + "}");
      XmlHelper.setAttribute(elem, ATTR_TAG_REF_NAME, name);

      elem.setAttribute(ATTR_TAG_REF_TYPE, rh.getType());
      XmlHelper.setAttribute(elem, ATTR_TAG_REF_VAL, rh.getValue());

      elem.setAttribute(ATTR_TAG_REF_LINE, Integer.toString(rh.getLine()));
      elem.setAttribute(ATTR_TAG_REF_COL, Integer.toString(rh.getColumn()));
   }


   /**
    * Creates the symbol hint element.
    * Creation of this hint is done under the current parent element.
    *
    * @param name
    *        the symbol's name
    * @param b
    *        The symbol's value.
    */
   public void createConstantSymbol(String name, Symbol b)
   {
      if (b == null || b.getValue() == null)
      {
         return;
      }

      Element parent = (Element)parents.peek();

      Element elem = dom.createElement(ELEM_TAG_SYM);
      elementsCount ++;
      parent.appendChild(elem);
      
      XmlHelper.setAttribute(elem, ATTR_TAG_SYM_NAME, name);

      elem.setAttribute(ATTR_TAG_SYM_TYPE, b.getOriginText());
      XmlHelper.setAttribute(elem, ATTR_TAG_SYM_VAL, b.getValue());
   }

   /**
    * Saves the hints tree to the file.
    *
    * @throws  IOException
    *          Forwarded from XmlHelper.
    */
   public void save()
   throws IOException
   {
      if (filename != null && elementsCount > 0)
         XmlHelper.write(dom, filename);
   }
}