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