# ApplicationError.java

    /*
     ** Module   : ApplicationError.java
     ** Abstract : Implementation of the OpenEdge.Core.System.ApplicationError class.
     **
     ** Copyright (c) 2018-2023, Golden Code Development Corporation.
     **
    ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
    ** 001 ME  20200402 First version.
    **         20201214 Fix class map, set severity and override toString, clone.
    ** 002 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy  signature.
    **     CA  20220120 Do not used TypeFactory.object when the OO reference must not be tracked or registered.
    **                  Do not use TypeFactory.object for internal usages, use ObjectVar if the reference must
    **                  be tracked.
    **                  All TypeFactory.object variable definitions must be done outside of the top-level block.
    **     VVT 20221221 Serialization support added (see #4658).
    **     RFB 20221223 Minor javadoc repair
    **     CA  20220513 Renamed _BaseObject_.clone() to legacyClone(), as it can be overriden by sub-classes and
    **                  the access mode may collide with the Java's Object.clone().
    ** 003 ME  20230503 Use the getter for security (made private #6410).
    ** 004 CA  20231113 The 'execute' method must be annotated with LegacySignature Type.Execute, and also can be
    **                  dropped if is a no-op.
    */

    /*
     ** 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.oo.core.system;

    import com.goldencode.p2j.util.*;
    import com.goldencode.p2j.util.BlockManager.Action;
    import com.goldencode.p2j.util.BlockManager.Condition;

    import static com.goldencode.p2j.report.ReportConstants.*;
    import static com.goldencode.p2j.util.BlockManager.*;
    import static com.goldencode.p2j.util.InternalEntry.Type;

    import java.io.*;
    import java.util.HashMap;
    import java.util.Map;

    import com.goldencode.p2j.oo.lang.*;
    import com.goldencode.p2j.security.ContextLocal;

    /**
     * Business logic (converted to Java from the 4GL source code
     * in OpenEdge/Core/System/ApplicationError.cls).
     */
    @LegacyResource(resource = "OpenEdge.Core.System.ApplicationError")
    @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
    public abstract class ApplicationError
    extends com.goldencode.p2j.oo.lang.AppError
    {
       // this could be a plain static object maybe, not sure if context local works
       private static ContextLocal<Map<Class, String[]>> errorDefinitions = new ContextLocal<Map<Class, String[]>>()
       {
          @Override
          protected Map<Class, String[]> initialValue()
          {
             return new HashMap<Class, String[]>();
          }
       };

       private object<? extends com.goldencode.p2j.oo.lang.LegacyError> innerError = TypeFactory
                .object(com.goldencode.p2j.oo.lang.LegacyError.class);

       @LegacySignature(type = Type.EXECUTE)
       public void __core_system_ApplicationError_execute__()
       {
          onBlockLevel(Condition.ERROR, Action.THROW);
       }

       @LegacySignature(returns = "OBJECT", qualified = "Progress.Lang.Error", type = Type.GETTER, name = "InnerError")
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
       public object<? extends com.goldencode.p2j.oo.lang.LegacyError> getInnerError()
       {
          return function(ApplicationError.class, this, "InnerError", object.class,
                   new Block((Body) () -> {
                      returnNormal(innerError);
                   }));
       }

       @LegacySignature(type = Type.METHOD, name = "AddError", parameters = {
                @LegacyParameter(name = "poType", type = "OBJECT", qualified = "progress.lang.class", mode = "INPUT"),
                @LegacyParameter(name = "pcShortMessage", type = "CHARACTER", mode = "INPUT"),
                @LegacyParameter(name = "pcMessage", type = "LONGCHAR", mode = "INPUT") })
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
       protected static void addError(final object<? extends LegacyClass> _poType,
                final character _pcShortMessage, final longchar _pcMessage)
       {
          ObjectOps.load(ApplicationError.class);

          internalProcedure(ApplicationError.class, "AddError", new Block((Body) () -> {
             if (!_poType.isUnknown())
             {
                final Class cls = _poType.ref().getType();
                final String pcShortMessage = _pcShortMessage.isUnknown() ? "" : _pcShortMessage.getValue();
                final String pcMessage = _pcMessage.isUnknown() ? "" : _pcMessage.getValue();

                errorDefinitions.get().put(cls, new String[] { pcShortMessage, pcMessage });
             }
          }));
       }

       @LegacySignature(type = Type.CONSTRUCTOR)
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
       public static void __core_system_ApplicationError_constructor__static__()
       {
          externalProcedure(ApplicationError.class, new Block((Body) () -> {
             onBlockLevel(Condition.ERROR, Action.THROW);
             ApplicationError.addError(new object<LegacyClass>(ObjectOps.getClassInstance(ApplicationError.class)),
                      new character("Application Error"),
                      new longchar("Default Application Error: &1 &2 &3 &4 &5 &6 &7 &8 &9"));
          }));
       }

       @LegacySignature(type = Type.CONSTRUCTOR)
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
       public void __core_system_ApplicationError_constructor__()
       {
          internalProcedure(ApplicationError.class, this,
                   "__core_system_ApplicationError_constructor__", new Block((Body) () -> {
                      __lang_AppError_constructor__();
                      setSeverity(new integer(ErrorSeverityEnum.default_.ref()._getValue()));
                   }));
       }

       @LegacySignature(type = Type.CONSTRUCTOR, parameters = {
                @LegacyParameter(name = "p1", type = "OBJECT", qualified = "progress.lang.error", mode = "INPUT") })
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
       public void __core_system_ApplicationError_constructor__(
                final object<? extends com.goldencode.p2j.oo.lang.LegacyError> _p1)
       {
          internalProcedure(ApplicationError.class, this,
                   "__core_system_ApplicationError_constructor__", new Block((Body) () -> {
                      __core_system_ApplicationError_constructor__();
                      this.innerError.assign(_p1);
                   }));
       }

       @LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetShortMessage")
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
       public character getShortMessage()
       {
          return function(ApplicationError.class, this, "GetShortMessage", character.class,
                   new Block((Body) () -> {
                      final String[] errorDef = getErrorDefinition();

                      if (errorDef != null && errorDef[0] != null)
                      {
                         returnNormal(TextOps.substitute(errorDef[0], getMessage(new integer(1)),
                                  getMessage(new integer(2)), getMessage(new integer(3)),
                                  getMessage(new integer(4)), getMessage(new integer(5)),
                                  getMessage(new integer(6)), getMessage(new integer(7)),
                                  getMessage(new integer(8)), getMessage(new integer(9))));
                      }

                      returnNormal(new character(""));
                   }));
       }

       @LegacySignature(returns = "LONGCHAR", type = Type.METHOD, name = "GetErrorMessage")
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
       public longchar getErrorMessage()
       {
          return function(ApplicationError.class, this, "GetErrorMessage", longchar.class,
                   new Block((Body) () -> {
                      String[] errorDef = getErrorDefinition();

                      if (errorDef != null && errorDef[1] != null)
                      {
                         returnNormal(TextOps.substitute(errorDef[1], getMessage(new integer(1)),
                                  getMessage(new integer(2)), getMessage(new integer(3)),
                                  getMessage(new integer(4)), getMessage(new integer(5)),
                                  getMessage(new integer(6)), getMessage(new integer(7)),
                                  getMessage(new integer(8)), getMessage(new integer(9))));
                      }
                      returnNormal(new longchar(""));
                   }));
       }

       @LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "ToString")
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
       @Override
       public character toLegacyString()
       {
          return getShortMessage();
       }

       @LegacySignature(returns = "OBJECT", qualified = "Progress.Lang.Object", type = Type.METHOD, name = "Clone")
       @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
       @Override
       public object<? extends _BaseObject_> legacyClone()
       {
          object<ApplicationError> err = TypeFactory.object(ApplicationError.class);

          return function(ApplicationError.class, this, "Clone", object.class, new Block((Body) () ->
          {
             err.assign(ObjectOps.newInstance(getClass(), "I", innerError));
             err.ref().setSeverity(getSeverity());
             err.ref().setReturnValue(getReturnValue());

             for (int i = 0; i < getNumMessages().intValue(); i++)
             {
                integer idx = new integer(i + 1);
                // message number seems to be ignored
                err.ref().addMessage(getMessage(idx), idx);
             }

             returnNormal(err);
          }));
       }

       /**
        * The object implements the writeExternal method to save its contents
        * by calling the methods of DataOutput for its primitive values or
        * calling the writeObject method of ObjectOutput for objects, strings,
        * and arrays.
        *
        * @serialData Overriding methods should use this tag to describe
        *             the data layout of this Externalizable object.
        *             List the sequence of element types and, if possible,
        *             relate the element to a public/protected field and/or
        *             method of this Externalizable class.
        *
        * @param out the stream to write the object to
        * @exception IOException Includes any I/O exceptions that may occur
        */
       @Override
       public void writeExternal(final ObjectOutput out)
       throws IOException
       {
          super.writeExternal(out);

          out.writeObject(innerError);
       }

       /**
        * The object implements the readExternal method to restore its
        * contents by calling the methods of DataInput for primitive
        * types and readObject for objects, strings and arrays.  The
        * readExternal method must read the values in the same sequence
        * and with the same types as were written by writeExternal.
        *
        * @param in the stream to read data from in order to restore the object
        * @exception IOException if I/O errors occur
        * @exception ClassNotFoundException If the class for an object being
        *              restored cannot be found.
        */
       @Override
       public void readExternal(final ObjectInput in)
       throws IOException,
              ClassNotFoundException
       {
          super.readExternal(in);

          innerError = (object<? extends LegacyError>) in.readObject();
       }

       /**
        * Get error definition strings previously added in {@link #addError(object, character, longchar)}.
        *
        * @return see above
        */
       protected String[] getErrorDefinition()
       {
          final Map<Class, String[]> defs = errorDefinitions.get();

          return defs.getOrDefault(this.getClass(), defs.get(ApplicationError.class));
       }
    }