Options.java

/*
** Module   : Options.java
** Abstract : stores E4GL options specified in meta/wsmeta elements
**
** Copyright (c) 2007-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ---------------------------------Description-----------------------------------
** 001 GES 20070822   @34967 Store all options parsed from an E4GL file.
** 002 GES 20241123          Added default wsoptions if none are manually specified and there is no meta tag.
*/ 

/*
** 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.e4gl;

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

/**
 * Provides a simple mechanism to store all options parsed from an E4GL file.
 * <p>
 * These options can be manipulated and set externally as well.
 */
public class Options
implements E4GLConstants
{
   /** Compatibility mode (see {@link E4GLConstants}). */
   private int mode = DEFAULT_MODE;
   
   /** Flag controlling whether this emits as a procedure or include file. */  
   private boolean include = false;
   
   /** Content-Type meta element is left alone (not commented out). */
   private boolean keepContentType = false;
   
   /** Encoded content type found in meta element. */
   private String contentType = "text/html";
   
   /** Encoded wsoptions found in meta element. */
   private Set<String> wsoptions = new LinkedHashSet<String>();
   
   /** Input HTML filename. */
   private String input = null;
   
   /** Output HTML filename. */       
   private String output = null;
   
   /** Operating system name for which preprocessing must account. */       
   private String opsys = null;
   
   /** Helper to emit text that exactly matches a given implementation. */
   private CompatibilityHelper hlp = null;
   
   /**
    * Create an instance that processes options with <code>WEBSPEED</code>
    * compatibility.
    */
   public Options()
   {
   }
   
   /**
    * Create an instance that processes options with the specified
    * compatibility.
    *
    * @param    mode
    *           Compatibility mode (see {@link E4GLConstants}).
    */
   public Options(int mode)
   {
      this.mode = mode;
   }
   
   /**
    * Access the compatibility mode (see {@link E4GLConstants}).
    *
    * @return   The compatibility mode for this HTML file.
    */
   public int getMode()
   {
      return mode;
   }
   
   /**
    * Set the compatibility mode (see {@link E4GLConstants}). If this mode
    * is different from the previously set value, the compatibility helper
    * reference is set to <code>null</code>. Any following call to
    * {@link #getCompatibilityHelper} will subsequently return a new instance
    * that matches the new mode.
    *
    * @param    mode
    *           The compatibility mode for this HTML file.
    */
   public void setMode(int mode)
   {
      if (this.mode != mode)
      {
         hlp = null;
      }
      
      this.mode = mode;
   }
   
   /**
    * Access the specified input filename.
    *
    * @return   A filename.
    */
   public String getInput()
   {
      return input;
   }
   
   /**
    * Set the specified input filename.
    *
    * @param    input
    *           A filename.
    */
   public void setInput(String input)
   {
      this.input = input;
   }
   
   /**
    * Access the output filename.
    *
    * @return   A filename.
    */
   public String getOutput()
   {
      return output;
   }
   
   /**
    * Set the specified output filename. WARNING: this will override any
    * previously calculated/specified output filename.
    *
    * @param    output
    *           A filename.
    */
   public void setOutput(String output)
   {
      this.output = output;
   }
   
   /**
    * Gets the operating system name which must be used to determine how
    * escape characters are treated.
    *
    * @return   The operating system name.
    */
   public String getOpsys()
   {
      if (opsys == null)
         opsys = EnvironmentOps.getOperatingSystem().toStringMessage();
      
      return opsys;
   }
   
   /**
    * Sets the operating system name which must be used to determine how
    * escape characters are treated.
    *
    * @param    opsys
    *           The new operating system name.
    */
   public void setOpsys(String opsys)
   {
      this.opsys = opsys;
   }
   
   /**
    * Parse the options string (as found in a meta element) and set the flags
    * and state variables as needed based on the options specified. This
    * string is normally a comma-separated list of options and this list
    * will be cleaned up, merged and stored as a master options string in
    * addition to the flags that will be set.
    * <p>
    * Since this code safely merges options with any that were previously
    * stored, this is suitable for handling multiple meta elements in the
    * same file (and additionally, any number of manual calls to add
    * options).
    * <p>
    * This can be used to manually add a list of options (as opposed to
    * reading these options from the meta element of an HTML file).
    * <p>
    * Processing is always additive. There is no way to delete options once
    * set.
    *
    * @param    options
    *           A comma-separated list of options.
    * @param    html
    *           <code>true</code> if <code>wsoptions</code> was read from an
    *           HTML file meta element. In that case, the "wsoptions-found"
    *           will be appended to the options string if it is not already
    *           present in the options.
    */
   public void processWSOptions(String options, boolean html)
   {
      String[] list = options.split(",", 0);
      
      for (int i = 0; i < list.length; i++)
      {
         list[i] = list[i].trim();
         if (list[i].length() > 0)
            wsoptions.add(list[i].toLowerCase());
      }
      
      if (html)
      {
         wsoptions.add(OPT_FOUND);
      }
      
      // read our resulting set of options (must be done here to honor the
      // cumulative options)
      boolean incl = wsoptions.contains(OPT_INCLUDE);
      boolean webo = wsoptions.contains(OPT_WEB_OBJ);
      
      if (html && mode != BLUE_DIAMOND)
      {
         wsoptions.add(OPT_FOUND);
      }
      
      keepContentType = wsoptions.contains(OPT_KEEP);
      
      // process OPT_INCLUDE and OPT_WEB_OBJ options
      if (mode != BLUE_DIAMOND)
      {
         // if neither are specified or if web-object is present, then
         // generate a web-object, if only include is specified then generate
         // an include
         include = ((!incl && !webo) || webo) ? false : true;
         
         if (!incl && !webo)
         {
            wsoptions.add(OPT_WEB_OBJ);
         }
      }
      else
      {
         // the presence of the include option sets include mode
         if (incl)
            include = true;
      }
   }
   
   /**
    * Access the <code>wsoptions</code> string.
    * <p>
    * Try to call this only once (or at least infrequently) since it
    * dynamically builds the options string off an internal collection
    * of options. This is much more expensive than just returning a
    * string member.
    *
    * @return   The complete set of options as a comma-separated list.
    */
   public String getWSOptions()
   {
      // for webspeed, set some defaults if there were no manually set options or a meta tag
      if (wsoptions.size() == 0 && mode != BLUE_DIAMOND)
      {
         wsoptions.add(output);
         wsoptions.add(OPT_WEB_OBJ);
      }
      
      Iterator<String> iter = wsoptions.iterator();
      
      StringBuilder sb = new StringBuilder();
      
      // iterate through the list and build a single options string
      while (iter.hasNext())
      {
         // add our separator if needed
         if (sb.length() > 0)
            sb.append(',');
         
         // add the next option
         sb.append(iter.next());
      }
      
      return sb.toString();
   }
   
   /**
    * Access the <code>content-type</code> string.
    *
    * @return   The content-type for this HTML file.
    */
   public String getContentType()
   {
      return contentType;
   }
   
   /**
    * Set the <code>content-type</code> string.
    *
    * @param    contentType
    *           The content-type for this HTML file.
    */
   public void setContentType(String contentType)
   {
      this.contentType = contentType;
   }
   
   /**
    * Determine if the generated 4GL code should be an include file or a
    * procedure file ("web-object").
    *
    * @return   <code>true</code> if this is an include file.
    */
   public boolean isInclude()
   {
      return include;
   }
   
   /**
    * Set the flag for whether the generated 4GL code should be an include
    * file or a procedure file ("web-object").
    *
    * @param    include
    *           <code>true</code> if this is an include file.
    */
   public void setInclude(boolean include)
   {
      this.include = include;
   }
   
   /**
    * Determine if the generated 4GL code should have the content-type meta
    * element left behind.
    *
    * @return   <code>true</code> if the content-type meta element is to be
    *           left uncommented in the generated code.
    */
   public boolean isKeepContentType()
   {
      return keepContentType;
   }
   
   /**
    * Set the flag for whether the generated 4GL code should have the
    * content-type meta element left behind.
    *
    * @param    keepContentType
    *           <code>true</code> if the content-type meta element is to be
    *           left uncommented in the generated code.
    */
   public void setKeepContentType(boolean keepContentType)
   {
      this.keepContentType = keepContentType;
   }
   
   /**
    * Obtain the current compatibility helper instance based on the mode
    * that was specified. The helper will be instantiated and initialized
    * the first time this is called and the same helper instance will be
    * returned thereafter unless the compatibility mode is changed. See
    * {@link #setMode}.
    *
    * @return   The helper to use for emitting compatible text.
    */
   public CompatibilityHelper getCompatibilityHelper()
   {
      if (hlp == null)
      {
         if (mode == WEBSPEED)
         {
            hlp = new WebSpeedHelper();
         }
         else if (mode == WEBSPEED_STRICT)
         {
            hlp = new WebSpeedStrictHelper();
         }
         else if (mode == BLUE_DIAMOND)
         {
            hlp = new BlueDiamondHelper();
         }
         else
         {
            hlp = new BaseHelper();
         }
         
         hlp.initialize(this);
      }
      
      return hlp;
   }
   
   /**
    * Create a text representation of the state of this instance.
    *
    * @return   Text representation of all state.
    */
   public String toString()
   {
      StringBuilder sb = new StringBuilder();
      
      sb.append(String.format("Options instance (0x%08X); ", hashCode()));
      sb.append(String.format("compat mode = %s; ", MODE_NAMES[mode]));
      sb.append(String.format("include = %b; ", include));
      sb.append(String.format("input file = %s; ", input));
      sb.append(String.format("output file = %s; ", output));
      sb.append(String.format("keep content type = %b; ", keepContentType));
      sb.append(String.format("content-type = '%s'; ", contentType));
      sb.append(String.format("wsoptions = '%s'; ", getWSOptions()));
      
      return sb.toString();
   }
}