BuiltinVariable.java

/*
** Module   : BuiltinVariable.java
** Abstract : the pool of Progress preprocessor built-in variables
**
** Copyright (c) 2004-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------Description------------------------------
** 001 NVS 20041124   @18757 Created. Initial implementation of the class.
** 002 GES 20070320   @32502 Changed OPSYS implementation to use the standard runtime support 
**                           instead of a hard coded value.  Added support for the 4 webspeed 
**                           related built-in variables.
** 003 GES 20070717   @34575 Many code formatting cleanups. Additions to treat the webspeed vars 
**                           as redefinable.
** 004 GES 20070816   @34885 Complete rewrite to simplify the implementation. Added complete 
**                           webspeed support. Fixed sequence support so that it resets to 0 for 
**                           each top-level file. Added complete support for all builtins which
**                           previously were hard coded to values for expediency. Now the built-in
**                           support is 100% complete through the 10.1B level.
** 005 ECF 20070912   @35050 Made definition of webspeed definitions configurable. These are only
**                           defined if the configuration parameter 'ws-preproc-syms' is set to 
**                           'true'.
** 006 CA  20140320          Fixed the FILE-NAME variable: this will be replaced with the 
**                           base-path relative name of the file in which FILE-NAME was used.
** 007 OM  20150508          Increment &SEQUENCE only if env.isExpandBraces().
** 008 EVL 20160223          Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
** 009 GES 20180105          Added an implicit globally defined symbol (that can be redefined by
**                           4GL code if it conflicts with their usage) that allows the detection
**                           of FWD and the FWD version from 4GL code. This will make it easy for
**                           conditional preprocessing to allow inclusion of FWD-specific 4GL
**                           code.
** 010 CA  20180321          Added support for PROCESS-ARCHITECTURE preprocessor var.
** 011 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
*/

/*
** 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 com.goldencode.p2j.*;
import com.goldencode.p2j.cfg.*;
import com.goldencode.p2j.convert.*;
import com.goldencode.p2j.util.*;

import java.io.*;
import java.util.logging.*;

/**
 * Represents the pool of Progress preprocessor builtin variables.
 * A preprocessor implementation needs an instance of this class.
 * The class provides methods for identifying the builtin variables
 * by name and producing corresponding strings as their values.
 * <p>
 * As the builtin variables can be overridden using include file
 * references, the scoping of their names is as important as of any
 * other variable name. For this reason, all builtin variables are
 * put into the global scope of a dictionary specified as a parameter
 * when constructing objects of this class.
 * <p>
 * Producing values for some of the variables requires access to
 * some information maintained by the preprocessor on a per file
 * basis. This access is done through a reference to the Environment
 * object.
 * <p>
 * Supported Progress 4GL built-in variables:
 * <p>
 * <pre>
 * BATCH-MODE  
 * FILE-NAME  
 * LINE-NUMBER 
 * OPSYS       
 * PROCESS-ARCHITECTURE
 * SEQUENCE
 * WINDOW-SYSTEM
 * </pre>
 * <p>
 * Supported WebSpeed global defines:
 * <p>
 * <pre>
 * WEBSTREAM
 * OUT 
 * OUT-FILE 
 * OUT-STREAM
 * OUT-FMT  
 * OUT-LONG 
 * DISPLAY  
 * END   
 * AMP      
 * LT       
 * GT       
 * QUOT     
 * </pre>
 * <p>
 * WARNING: the web-speed built-in variables are not really preprocessor
 * built-ins in Progress. In particular, they can be re-defined as symbols
 * when the others cannot. Although the Progress documentation calls them
 * built-in, it these are actually implemented in include files rather than
 * in the preprocessor/runtime. We place these implementations here as a
 * default, but allow any webspeed or webspeed clone code to naturally
 * override these definitions when it is preprocessed.
 * <p>
 * Supported FWD-specific global defines:
 * <p>
 * <pre>
 * FWD-VERSION (this will have a value defined by {@link Version#getFWDVersion})
 * </pre>
 * <p>
 * The FWD-specific symbols are always implicitly there but they are designed to be overridden
 * if needed (they are deliberately registered as a global define).
 */
public class BuiltinVariable
{
   /** SpeedScript (embedded 4GL) &amp;WEBSTREAM definition. */
   private static final String WEBSTREAM = "STREAM WebStream";
   
   /** SpeedScript (embedded 4GL) &amp;OUT-FMT definition. */
   private static final String OUT_FMT = "PUT " + WEBSTREAM;
   
   /** SpeedScript (embedded 4GL) &amp;OUT definition. */
   private static final String OUT = OUT_FMT + " UNFORMATTED";
   
   /** SpeedScript (embedded 4GL) &amp;OUT-LONG definition. */
   private static final String OUT_LONG = "EXPORT " + WEBSTREAM;
   
   /** SpeedScript (embedded 4GL) &amp;DISPLAY definition. */
   private static final String DISPLAY = "DISPLAY " + WEBSTREAM;
   
   /** SpeedScript (embedded 4GL) &amp;END definition. */
   private static final String END = ".";
   
   /** SpeedScript (embedded 4GL) &amp;AMP definition. */
   private static final String AMP = "&amp~;";
   
   /** SpeedScript (embedded 4GL) &amp;LT definition. */
   private static final String LT = "&lt~;";
   
   /** SpeedScript (embedded 4GL) &amp;GT definition. */
   private static final String GT = "&gt~;";
   
   /** SpeedScript (embedded 4GL) &amp;QUOT definition. */
   private static final String QUOT = "&quot~;";
   
   /** FWD-VERSION global define value. */
   private static final String FWD_VERSION_VALUE = Version.getFWDVersion();
   
   /** Logger */
   private static final ConversionStatus LOG = ConversionStatus.get(BuiltinVariable.class);
   
   /**
    * Adds all WebSpeed and 4GL built-in variables to the global scope of the
    * given dictionary with the <code>Symbol.GLOBAL</code> or
    * <code>Symbol.BUILTIN</code> as the origin respectively.
    *
    * @param    sym
    *           Dictionary where the names of all the variables are added.
    */
   public static void init(ScopedSymbolDictionary sym)
   {
      // add the webspeed definitions, since they aren't marked as builtin
      // they can be redefined in user code, but only if webspeed preproc
      // symbols are enabled in the configuration
      if ("true".equals(Configuration.getParameter("ws-preproc-syms")))
      {
         sym.addSymbol(true, "webstream",  new Symbol(Symbol.GLOBAL, WEBSTREAM));
         sym.addSymbol(true, "out",        new Symbol(Symbol.GLOBAL, OUT));
         sym.addSymbol(true, "out-file",   new Symbol(Symbol.GLOBAL, OUT));
         sym.addSymbol(true, "out-stream", new Symbol(Symbol.GLOBAL, OUT));
         sym.addSymbol(true, "out-fmt",    new Symbol(Symbol.GLOBAL, OUT_FMT));
         sym.addSymbol(true, "out-long",   new Symbol(Symbol.GLOBAL, OUT_LONG));
         sym.addSymbol(true, "display",    new Symbol(Symbol.GLOBAL, DISPLAY));
         sym.addSymbol(true, "end",        new Symbol(Symbol.GLOBAL, END));
         sym.addSymbol(true, "amp",        new Symbol(Symbol.GLOBAL, AMP));
         sym.addSymbol(true, "lt",         new Symbol(Symbol.GLOBAL, LT));
         sym.addSymbol(true, "gt",         new Symbol(Symbol.GLOBAL, GT));
         sym.addSymbol(true, "quot",       new Symbol(Symbol.GLOBAL, QUOT));
      }
      
      // add FWD-specific global defines (always implicitly there, can be overridden)
      sym.addSymbol(true, "fwd-version",   new Symbol(Symbol.GLOBAL, FWD_VERSION_VALUE));
   
      // add the real built-ins
      sym.addSymbol(true, "batch-mode"          , new BatchMode()   );
      sym.addSymbol(true, "file-name"           , new FileName()    );
      sym.addSymbol(true, "line-number"         , new LineNumber()  );
      sym.addSymbol(true, "opsys"               , new Opsys()       );
      sym.addSymbol(true, "sequence"            , new Sequence()    );
      sym.addSymbol(true, "window-system"       , new WindowSystem());
      sym.addSymbol(true, "process-architecture", new ProcessArchitecture());
   }
   
   /**
    * Produces the BATCH-MODE value.
    */
   private static class BatchMode
   extends BuiltinSymbol
   {
      /**
       * Gets the symbol value using the current environment to dynamically
       * create the result.
       *
       * @param    env
       *           The preprocessor's current environment.
       *
       * @return   The symbol's value.
       */
      public String getValue(Environment env)
      {
         return env.getOpt().isBatchMode() ? "yes" : "no";
      }
   }

   /**
    * Produces the FILE-NAME value.
    */
   private static class FileName
   extends BuiltinSymbol
   {
      /**
       * Gets the symbol value using the current environment to dynamically
       * create the result.
       *
       * @param    env
       *           The preprocessor's current environment.
       *
       * @return   The current file name as specified in command line or
       *           include file reference.
       */
      public String getValue(Environment env)
      {
         ScopedSymbolDictionary sym = env.getSym();
         FileScope fs = (FileScope) sym.getScope();
         String normalized = null;
   
         String base = env.getOpt().getBasepath();

         try
         {
            // take the base-path relative part of the filename.
            normalized = Configuration.normalizeFilename(fs.getFile().getCanonicalPath());
            normalized = normalized.substring(base.length());
            if (normalized.startsWith(File.separator))
            {
               normalized = normalized.substring(1);
            }
         }
         
         catch (IOException e)
         {
            env.eprint("Exception caught in &{FILE_NAME} builtin variable",
                       env.getLex().getLine(),
                       env.getLex().getColumn());
            LOG.log(Level.INFO, "", e);
            normalized = fs.getFileName();
         }
   
         return normalized;
      }
   }

   /**
    * Produces the LINE-NUMBER value.
    */
   private static class LineNumber
   extends BuiltinSymbol
   {
      /**
       * Gets the symbol value using the current environment to dynamically
       * create the result.
       *
       * @param    env
       *           The preprocessor's current environment.
       *
       * @return   The current line number.
       */
      public String getValue(Environment env)
      {
         return Integer.toString(env.getLex().getLine());
      }
   }

   /**
    * Produces the OPSYS value.
    */
   private static class Opsys
   extends BuiltinSymbol
   {
      /**
       * Gets the symbol value using the current environment to dynamically
       * create the result.
       *
       * @param    env
       *           The preprocessor's current environment.
       *
       * @return   The operating system name.
       */
      public String getValue(Environment env)
      {
         return env.getOpt().getOpsys();
      }
   }
   
   /**
    * Produces the SEQUENCE value, incrementing the value each time it is
    * referenced. The previous value is returned. The first value returned
    * is always 0 and this is reset for each compilation unit (top level
    * file from the perspective of the preprocessor).
    */
   private static class Sequence
   extends BuiltinSymbol
   {
      /** The current sequence counter. */
      private int sequence = 0;
      
      /**
       * Gets the symbol value using the current environment to dynamically
       * create the result.
       *
       * @param    env
       *           The preprocessor's current environment.
       *
       * @return   The current sequence number.
       */
      public String getValue(Environment env)
      {
         return Integer.toString(env.isExpandBraces() ? sequence++ : sequence);
      }
   }
   
   /**
    * Produces the WINDOW-SYSTEM value.
    */
   private static class WindowSystem
   extends BuiltinSymbol
   {
      /**
       * Gets the symbol value using the current environment to dynamically
       * create the result.
       *
       * @param    env
       *           The preprocessor's current environment.
       *
       * @return   The WINDOW-SYSTEM value.
       */
      public String getValue(Environment env)
      {
         return env.getOpt().getWindowSystem();
      }
   }

   /**
    * Produces the PROCESS-ARCHITECTURE value.
    */
   private static class ProcessArchitecture
   extends BuiltinSymbol
   {
      /**
       * Gets the symbol value using the current environment to dynamically
       * create the result.
       *
       * @param    env
       *           The preprocessor's current environment.
       *
       * @return   The PROCESS-ARCHITECTURE value.
       */
      public String getValue(Environment env)
      {
         return Integer.toString(env.getOpt().getProcessArchitecture());
      }
   }
}