=== modified file 'rules/adm/adm_windows.xml'
--- old/rules/adm/adm_windows.xml	2025-02-26 12:13:08 +0000
+++ new/rules/adm/adm_windows.xml	2025-05-09 07:59:16 +0000
@@ -33,6 +33,8 @@
 ** 014 CA  20250226 Allow all ADM windows to be processed, regardless if they have parameters or not.  The
 **                  fwd-embedded-driver.p and associated emain.p provides ways to run the window with
 **                  parameters (by saving all the necessary info at the target 'module').
+** 015 DDF 20250423 Replaced filename with artifact.
+**     DDF 20250424 Replaced file with artifact.
 */
 -->
 
@@ -355,7 +357,7 @@
       
       <post-rules>
          <rule>embeddedWindow
-            <action>embedded.put(this.filename, create("java.util.HashMap"))</action>
+            <action>embedded.put(this.artifact.getRelativePath(), create("java.util.HashMap"))</action>
          </rule>
       </post-rules>
    </rule-set>
@@ -382,7 +384,7 @@
          <rule>windowName = null</rule>
          <rule>admVersion = null</rule>
          
-         <rule>embeddedDetails = embedded.get(this.filename)</rule>
+         <rule>embeddedDetails = embedded.get(this.artifact.getRelativePath())</rule>
 
          <rule>hints = preproc.getHints()</rule>
          <rule>hints != null and hints.getRoot() != null
@@ -403,39 +405,39 @@
             <rule>hints != null and hints.getRoot() != null
                <action>
                   printfln("WARNING: Removing %s because no FRAME-NAME symbol is found.", 
-                           this.filename)
+                           this.artifact.getRelativePath())
                </action>
             </rule>
             
-            <action>embedded.remove(this.filename)</action>
+            <action>embedded.remove(this.artifact.getRelativePath())</action>
          </rule>
 
          <rule>embeddedDetails != null and windowName == null
             <rule>hints != null and hints.getRoot() != null
                <action>
                   printfln("WARNING: Removing %s because no WINDOW-NAME symbol is found.", 
-                           this.filename)
+                           this.artifact.getRelativePath())
                </action>
             </rule>
 
-            <action>embedded.remove(this.filename)</action>
+            <action>embedded.remove(this.artifact.getRelativePath())</action>
          </rule>
          
          <rule>embeddedDetails != null and admVersion == null
             <rule>hints != null and hints.getRoot() != null
                <action>
                   printfln("WARNING: Removing %s because no ADM-VERSION symbol is found.", 
-                           this.filename)
+                           this.artifact.getRelativePath())
                </action>
             </rule>
 
-            <action>embedded.remove(this.filename)</action>
+            <action>embedded.remove(this.artifact.getRelativePath())</action>
          </rule>
          
-         <rule>embeddedDetails = embedded.get(this.filename)</rule>
+         <rule>embeddedDetails = embedded.get(this.artifact.getRelativePath())</rule>
          
          <rule>embeddedDetails != null
-            <action>embeddedName = this.filename</action>
+            <action>embeddedName = this.artifact.getRelativePath()</action>
 
             <!-- calc the name as it would be used in a Progress RUN statement 
                  (which would include any relative pathing such that a PROPATH
@@ -506,7 +508,7 @@
                <action>ref = this.getNextSibling()</action>
                <action>windowTitle = ecw.evaluateCharacterExpression(ref)</action>
                <rule>windowTitle == null
-                  <action>windowTitle = this.filename</action>
+                  <action>windowTitle = this.artifact.getRelativePath()</action>
                </rule>
 
                <action>embeddedDetails.put("window-title", windowTitle)</action>
@@ -541,9 +543,9 @@
             <rule>!programDetails.containsKey("window-ref-id")
                <action>
                   printfln("WARNING: dropping file %s as an embedded program because no window is set",
-                           file)
+                           artifact.getRelativePath())
                </action>
-               <action>embedded.remove(this.filename)</action>
+               <action>embedded.remove(this.artifact.getRelativePath())</action>
             </rule>
          </rule>
       </post-rules>
@@ -570,7 +572,7 @@
       <variable name="handleRefs"         type="java.util.Map" />
       
       <init-rules>
-         <rule>embeddedDetails = embedded.get(this.filename)</rule>
+         <rule>embeddedDetails = embedded.get(this.artifact.getRelativePath())</rule>
          
          <rule>embeddedDetails != null
             <rule>inAdmCreateObjects = false</rule>
@@ -656,7 +658,7 @@
                   <rule>currentPage &gt;= pages.size()
                      <action>
                         printfln("WARNING: %s has tried to access page %d with %d pages defined!", 
-                                 file, currentPage, pages.size())
+                                 artifact.getRelativePath(), currentPage, pages.size())
                      </action>
                   </rule>
                   
@@ -780,7 +782,7 @@
             </action>
          </rule>
       
-         <rule>procName = this.filename</rule>
+         <rule>procName = this.artifact.getRelativePath()</rule>
          <rule>embeddedDetails = embedded.get(procName)</rule>
 
          <!-- generate ASTs for this converted window -->

=== modified file 'rules/annotations/cleanup.rules'
--- old/rules/annotations/cleanup.rules	2024-03-30 16:59:34 +0000
+++ new/rules/annotations/cleanup.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : cleanup.rules
 ** Abstract : handles removal or hiding of unnecessary nodes
 **
-** Copyright (c) 2005-2024, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ __________________________________Description___________________________________
 ** 001 GES 20050816   @22140 Handles removal or hiding of unnecessary nodes.
@@ -118,6 +118,7 @@
 **                           versions, based on node types rather on string paths.
 ** 053 SVL 20240330          Fixed ENABLE/DISABLE statement hidden by mistake if all enabled elements have
 **                           WHEN condition.
+** 054 DDF 20250424          Replaced file with artifact.
 */
  -->
  
@@ -529,7 +530,7 @@
             <!-- uncomment following action if logging is required -->
             <!-- action>
                printfln("## INFO: %s: %d: hiding PAGED after PAGE-SIZE", 
-                        file, paged.line)
+                        artifact.getRelativePath(), paged.line)
             </action -->
             <action>paged.setHidden(true)</action>
          </rule>

=== modified file 'rules/annotations/collect_names.rules'
--- old/rules/annotations/collect_names.rules	2023-09-20 15:40:52 +0000
+++ new/rules/annotations/collect_names.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : collect_names.rules
 ** Abstract : collect Progress to Java name mappings and store for future use at runtime
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 SIY 20050818   @22167 Initial implementation of the name map collecting rule set. For each
@@ -65,6 +65,7 @@
 ** 028 OM  20230115          Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                           versions, based on node types rather on string paths.
 ** 029 ME  20230828          Add missing append/bind flags for table/dataset parameters.
+** 030 DDF 20250423          Replaced file with artifact.
 */
  -->
 
@@ -157,7 +158,7 @@
    <variable name="proot"          type="com.goldencode.p2j.convert.NameMappingWorker$MethodInfo" />
 
    <init-rules>
-      <rule>schema.initializeDictionary(file)</rule>
+      <rule>schema.initializeDictionary(artifact)</rule>
    
       <!-- gather file-level mapping data -->
       <rule>relativeName = getNoteString("relative-name")</rule>

=== modified file 'rules/annotations/copy_view_as_from_schema.rules'
--- old/rules/annotations/copy_view_as_from_schema.rules	2023-05-10 06:23:45 +0000
+++ new/rules/annotations/copy_view_as_from_schema.rules	2025-05-09 07:59:16 +0000
@@ -4,7 +4,7 @@
 ** Module   : copy_view_as_from_schema.rules
 ** Abstract : copy VIEW-AS phrases from the schema into the Progress AST
 **
-** Copyright (c) 2006-2023, Golden Code Development Corporation.
+** Copyright (c) 2006-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 GES 20060507   @26004 All user interface statements that can
@@ -28,6 +28,7 @@
 **                           and relaseDictionary brackets.
 ** 006 OM  20230115          Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                           versions, based on node types rather on string paths.
+** 007 DDF 20250423          Replaced file with artifact.
 */
 -->
 
@@ -106,7 +107,7 @@
    
    <init-rules>
       <!-- get schema dictionary initialized for this source file -->
-      <rule>sw.initializeDictionary(file)</rule>
+      <rule>sw.initializeDictionary(artifact)</rule>
       <rule>sdict = sw.getDictionary()</rule>
    </init-rules>
    

=== modified file 'rules/annotations/cross_namespace_conflicts.rules'
--- old/rules/annotations/cross_namespace_conflicts.rules	2023-05-10 07:41:38 +0000
+++ new/rules/annotations/cross_namespace_conflicts.rules	2025-05-09 07:59:16 +0000
@@ -6,7 +6,7 @@
 ** Abstract : resolves name conflicts that arise from a mismatch of Progress
 **            and Java namespaces
 **
-** Copyright (c) 2008-2023, Golden Code Development Corporation.
+** Copyright (c) 2008-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 GES 20080402   @37807 Resolves name conflicts that arise from a
@@ -27,6 +27,8 @@
 ** 008 CA  20200412          Added incremental conversion support.
 ** 009 OM  20230115          Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                           versions, based on node types rather on string paths.
+** 010 DDF 20250423          Use artifacts instead of the filename.
+**     DDF 20250423          Replaced missed filename with an artifact.
 */
 -->
 
@@ -86,12 +88,13 @@
 <rule-set>
 
    <!-- register worker objects -->
-   <worker class="com.goldencode.p2j.uast.ProgressPatternWorker"
-           namespace="prog" />
+   <worker class="com.goldencode.p2j.uast.ProgressPatternWorker" namespace="prog" />
+   <worker class="com.goldencode.artifacts.ArtifactWorker"       namespace="art" />
 
    <!-- variables -->
    <variable name="ref"           type="com.goldencode.ast.Aast" />
    <variable name="aref"          type="com.goldencode.ast.Aast" />
+   <variable name="refArtifact"   type="com.goldencode.artifacts.Artifact" />
    <variable name="incre"         type="java.lang.Long" />
    <variable name="refid"         type="java.lang.Long" />
    <variable name="varid"         type="java.lang.Long" />
@@ -101,6 +104,7 @@
    <variable name="newname"       type="java.lang.String" />
    <variable name="superCls"      type="java.lang.String" />
    <variable name="clsName"       type="java.lang.String" />
+   <variable name="fileName"      type="java.lang.String" />
    <variable name="counter"       type="java.util.Map" />
    <variable name="clsBufs"       type="java.util.Set" />
    <variable name="clsVars"       type="java.util.Set" />
@@ -144,7 +148,9 @@
          <rule>bufnames.contains(javaname)
          
             <action>
-               printfln("## INFO: %s: VAR/BUF name conflict for %s", ref.filename, javaname)
+               printfln("## INFO: %s: VAR/BUF name conflict for %s",
+                        ref.artifact.getRelativePath(),
+                        javaname)
             </action>
             
             <!-- we need to make the name unique -->
@@ -309,8 +315,10 @@
                   <while>aref.parent != null
                      <action>aref = aref.parent</action>
                   </while>
-                  
-                  <action>persist(aref, sprintf("%s.ast", aref.filename), true, false)</action>
+
+                  <action>fileName = sprintf("%s.ast", aref.artifact.getRelativePath())</action>
+                  <action>refArtifact = art.createArtifact(fileName)</action>
+                  <action>persist(aref, refArtifact, true, false)</action>
                </rule>
             </rule>
             

=== modified file 'rules/annotations/database_general.rules'
--- old/rules/annotations/database_general.rules	2025-03-19 13:22:37 +0000
+++ new/rules/annotations/database_general.rules	2025-05-09 07:59:16 +0000
@@ -141,6 +141,7 @@
 ** 061 SP  20241105          Where clause processing for loops that are converted to bulk deletes
 **                           should be using qualified field references in FQL.
 ** 062 CA  20250319          Fixed '-db "some\path\to\file\":U' option (when the string ends with :U).
+** 063 DDF 20250424          Replaced file with artifact.
 -->
 
 <!--
@@ -1025,7 +1026,10 @@
       <!-- drop QUERY-TUNING phrase, no equivalent in FWD -->
       <rule>this.type == prog.kw_qry_tune
          <action>
-            printfln("WARNING: dropping QUERY-TUNING phrase in %s @%s:%s", file, this.line, this.column)
+            printfln("WARNING: dropping QUERY-TUNING phrase in %s @%s:%s",
+                     artifact.getRelativePath(),
+                     this.line,
+                     this.column)
          </action>
          <action>copy.remove()</action>
       </rule>

=== modified file 'rules/annotations/early_annotations.xml'
--- old/rules/annotations/early_annotations.xml	2025-02-07 11:02:46 +0000
+++ new/rules/annotations/early_annotations.xml	2025-05-09 07:59:16 +0000
@@ -17,6 +17,7 @@
 ** 007 CA  20220427 Fixup constructor references, so they can be linked when called via THIS-OBJECT or SUPER.
 ** 008 RFB 20241107 Added abbrev_check rule. Ref. #9302.
 ** 009 DDF 20250207 Added annotations/early_javanames rule set.
+** 010 DDF 20250424 Replaced file with artifact.
 */
  -->
  
@@ -143,7 +144,7 @@
             <action>removeNote("tempidx-file")</action>
             <action>fprintf("post_parse_fixups.log",
                             "VAR_REF  %-40s: %-25s @ %05d : %05d\n",
-                            file,
+                            artifact.getRelativePath(),
                             this.getSymbolicTokenType(), 
                             line,
                             this.getColumn())
@@ -175,7 +176,7 @@
             <action>removeNote("tempidx-file")</action>
             <action>fprintf("post_parse_fixups.log",
                             "DS_REF  %-40s: %-25s @ %05d : %05d\n",
-                            file,
+                            artifact.getRelativePath(),
                             this.getSymbolicTokenType(), 
                             line,
                             this.getColumn())
@@ -200,7 +201,7 @@
             <action>removeNote("tempidx-file")</action>
             <action>fprintf("post_parse_fixups.log",
                             "METHOD_REF  %-40s: %-25s @ %05d : %05d\n",
-                            file,
+                            artifact.getRelativePath(),
                             this.getSymbolicTokenType(), 
                             line,
                             this.getColumn())

=== modified file 'rules/annotations/frame_scoping.rules'
--- old/rules/annotations/frame_scoping.rules	2025-01-10 07:45:20 +0000
+++ new/rules/annotations/frame_scoping.rules	2025-05-09 07:59:16 +0000
@@ -481,6 +481,7 @@
 ** 206 HC  20240703          Fixed conversion of widget reference in WEB-FILE-UPLOAD statement.
 ** 207 TJD 20230328          Do not backreference default frame for CLEAR.
 ** 208 LS  20250108          Added support for empty DISPLAY.
+** 209 DDF 20250424          Replaced file with artifact.
 */
 -->
 
@@ -1222,7 +1223,7 @@
             <action>to.put(fd, #(long) (-1))</action>
             <action>
                printfln("## INFO: %s: changing %s from 1 to 0 for %s", 
-                        file, fd,
+                        artifact.getRelativePath(), fd,
                         to.get(frameName))
             </action>
          </rule>
@@ -1233,7 +1234,7 @@
             <!-- TODO: FWD fails to change DOWN to the 'from's DOWN value, if explicitly set -->
             <action>
                printfln("## INFO: %s: attempt to change %s %d to %d for %s",
-                        file, fd,
+                        artifact.getRelativePath(), fd,
                         to.get(fd), 
                         from.get(fd), 
                         to.get(frameName))
@@ -1377,7 +1378,7 @@
                   parent.parent.parent == null
                <action>
                   printfln("## INFO: %s: assuming DOWN set to 1 for %s at %s", 
-                           file, var.get(frameName), this.lookupTokenName(this.type))
+                           artifact.getRelativePath(), var.get(frameName), this.lookupTokenName(this.type))
                </action>
                <rule>type == prog.define_frame
                   <action>
@@ -1460,8 +1461,15 @@
                      <action>frame = #(java.util.Map) 
                        lookupDictionaryObject(currScope, frameVar)
                     </action>
-                    <action>printfln("## WARN: %s: Using frame='%s' for %s/%s/%s (%s)", 
-                                     file, frame.get(frameName), ref.parent.parent, ref.parent, ref, text)</action>
+                    <action>
+                       printfln("## WARN: %s: Using frame='%s' for %s/%s/%s (%s)",
+                                artifact.getRelativePath(),
+                                frame.get(frameName),
+                                ref.parent.parent,
+                                ref.parent,
+                                ref,
+                                text)
+                    </action>
                  </rule>
                </rule>
             </rule>
@@ -4839,7 +4847,7 @@
          <rule>bbList.get(this.firstChild.text.toLowerCase()) != null
             <action on="false">
                printfln("## WARN: %s: unable to locate browse (%s)",
-                        file, firstChild.text)
+                        artifact.getRelativePath(), firstChild.text)
             </action>
             <action>tmp = execLib("search_widget", this.firstChild)</action>
             <action>evalLib("add_node_ex", tmp, copy.firstChild)</action>
@@ -5705,7 +5713,7 @@
          <rule>tmp != null
             <action on="false">
                printfln("## INFO: %s: unable to locate frame for %s/%s/%s %s",
-                        file, parent.parent, parent, this, text)
+                        artifact.getRelativePath(), parent.parent, parent, this, text)
             </action>
             <action>evalLib("add_node_ex", tmp, copy)</action>
             <action>evalLib("put_accessor_ex", copy, tmp)</action>
@@ -5722,7 +5730,7 @@
          <rule>tmp != null
             <action on="false">
                printfln("## INFO: %s: unable to locate frame %s/%s/%s %s",
-                        file, parent.parent, parent, this, text)
+                        artifact.getRelativePath(), parent.parent, parent, this, text)
             </action>
             <action>evalLib("add_node_ex", tmp, copy)</action>
             <action>evalLib("put_accessor_ex", copy, tmp)</action>
@@ -5743,7 +5751,7 @@
          <rule>tmp != null
             <action on="false">
                printfln("## INFO: %s: unable to locate frame %s/%s/%s %s",
-                        file, parent.parent, parent, this, text)
+                        artifact.getRelativePath(), parent.parent, parent, this, text)
             </action>
             <action>evalLib("add_node_ex", tmp, copy)</action>
             <action>evalLib("put_accessor_ex", copy, tmp)</action>
@@ -5829,7 +5837,7 @@
             
             <action on="false">
                printfln("## WARN: %s: no frame found for %s/%s/%s (%s)", 
-                        file, parent.parent, parent, this, text)
+                        artifact.getRelativePath(), parent.parent, parent, this, text)
             </action>
          </rule>
          

=== modified file 'rules/annotations/i18n.rules'
--- old/rules/annotations/i18n.rules	2023-05-10 07:41:38 +0000
+++ new/rules/annotations/i18n.rules	2025-05-09 07:59:16 +0000
@@ -5,12 +5,13 @@
 ** Module   : i18n.rules
 ** Abstract : i18n processing
 **
-** Copyright (c) 2021-2023, Golden Code Development Corporation.
+** Copyright (c) 2021-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 HC  20210926 Created initial version.
 ** 002 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 003 DDF 20250423 Replaced filename with artifact.
 -->
 
 <!--
@@ -106,7 +107,7 @@
 
          <!-- source reference -->
          <rule>txt.append("#: ")</rule>
-         <rule>txt.append(node.filename)</rule>
+         <rule>txt.append(node.artifact.getRelativePath())</rule>
          <rule>txt.append(":")</rule>
          <rule>txt.append(node.line)</rule>
          <rule>txt.append(":")</rule>
@@ -133,7 +134,12 @@
          <rule>txt.append("msgstr ")</rule>
          <rule>txt.append('"')</rule>
          <rule>targetLang != null
-            <rule>target = trw.resolveTMTranslation(targetLang, node.filename, node.line, progressMsg)</rule>
+            <rule>
+               target = trw.resolveTMTranslation(targetLang,
+                                                 node.artifact.getRelativePath(),
+                                                 node.line,
+                                                 progressMsg)
+            </rule>
             <rule>target != null
                <action>txt.append(sprintf("%s", target))</action>
             </rule>

=== modified file 'rules/annotations/implicit_where_clause.rules'
--- old/rules/annotations/implicit_where_clause.rules	2024-10-30 18:14:25 +0000
+++ new/rules/annotations/implicit_where_clause.rules	2025-05-09 07:59:16 +0000
@@ -4,7 +4,7 @@
 ** Module   : implicit_where_clause.rules
 ** Abstract : convert implicit where clauses into explicit additions
 **
-** Copyright (c) 2005-2020, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 GES 20051013   @23065 Convert implicit where clauses into explicit additions.
@@ -25,6 +25,7 @@
 ** 012 AL2 20240529          Release the schema dictionary at the very end. Otherwise, it will cause NPE
 **                           in other rule sets that use the schema dictionary at the same time.
 ** 013 ES  20241030          Added isFind parameter to pushRecordPhrase method.
+** 014 DDF 20250423          Replaced file with artifact.
 */
 -->
 
@@ -177,7 +178,7 @@
             
             <!-- init the schema dictionary and ensure it has the source
                  file specific temp-table data -->
-            <action>schemaw.initializeDictionary(file)</action>
+            <action>schemaw.initializeDictionary(artifact)</action>
             <action>schemaDict = schemaw.dictionary</action>
             <action>sym.schemaDictionary = schemaDict</action>
             

=== modified file 'rules/annotations/index_selection.rules'
--- old/rules/annotations/index_selection.rules	2024-11-07 15:38:11 +0000
+++ new/rules/annotations/index_selection.rules	2025-05-09 07:59:16 +0000
@@ -4,7 +4,7 @@
 ** Module   : index_selection.rules
 ** Abstract : duplicate the index selection decision of the Progress compiler 
 **
-** Copyright (c) 2004-2024, Golden Code Development Corporation.
+** Copyright (c) 2004-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 GES 20051014   @23066 Duplicates the index selection decision of
@@ -129,6 +129,7 @@
 **     RAA 20240904          Added risk factor for indeterminate sorting queries.
 **     RAA 20240910          When computing the order by, also receive a set with the legacy name criteria.
 ** 051 ES  20241030          Added isFind parameter to pushRecordPhrase method.
+** 052 DDF 20250423          Replaced file with artifact.
 */
 -->
 
@@ -351,7 +352,7 @@
       <rule>tablesList = create("java.lang.StringBuilder")</rule>
       <rule>countingTables = false</rule>
       <rule>tableCount = 0</rule>
-      <rule>schema.initializeDictionary(file)</rule>
+      <rule>schema.initializeDictionary(artifact)</rule>
       
       <rule>functypes = create("java.util.ArrayList")</rule>
       <rule>functypes.add(prog.func_char)</rule>

=== modified file 'rules/annotations/legacy_services.rules'
--- old/rules/annotations/legacy_services.rules	2025-02-19 19:52:02 +0000
+++ new/rules/annotations/legacy_services.rules	2025-05-09 07:59:16 +0000
@@ -32,6 +32,8 @@
 ** 008 CA  20230705 Legacy REST services which can be mapped in the .paar file(s) must be marked as "fwd", to
 **                  not be removed in legacy_annotations_post.rules. 
 ** 009 CA  20250219 Check if the file has an extension, when computing the file name for an operation.
+** 010 DDF 20250423 Replaced file with artifact.
+**     DDF 20250424 Replaced remaining file with artifact.
 */
  -->
  
@@ -329,7 +331,7 @@
    </func-library>
 
    <init-rules>
-      <rule>schema.initializeDictionary(file)</rule>
+      <rule>schema.initializeDictionary(artifact)</rule>
 
       <rule>deleteDictionary("annotation")</rule>
       
@@ -351,7 +353,7 @@
       </rule>
       
       <rule>oname = lower(oname)</rule>
-      <rule>iter = srv.getSoapOperations(file, oname, null).iterator()</rule>
+      <rule>iter = srv.getSoapOperations(artifact.getRelativePath(), oname, null).iterator()</rule>
       <while>iter.hasNext()
          <action>soapOp = iter.next()</action>
          
@@ -397,7 +399,7 @@
             <action>iename = #(java.lang.String) ref.getAnnotation("name")</action>
          </rule>
          
-         <rule>iter = srv.getSoapOperations(file, oname, iename).iterator()</rule>
+         <rule>iter = srv.getSoapOperations(artifact.getRelativePath(), oname, iename).iterator()</rule>
          <while>iter.hasNext()
             <action>soapOp = iter.next()</action>
             <action>hasSrvs = true</action>
@@ -568,7 +570,7 @@
       
       <rule>hasSrvs
          <action>putNote("with_services", true)</action>
-         <action>withServicesFile(file)</action>
+         <action>withServicesFile(artifact.getRelativePath())</action>
       </rule>
    </post-rules>
 </rule-set>

=== modified file 'rules/annotations/load_controls.rules'
--- old/rules/annotations/load_controls.rules	2020-02-25 13:35:40 +0000
+++ new/rules/annotations/load_controls.rules	2025-05-09 07:59:16 +0000
@@ -4,10 +4,11 @@
 ** Module   : load_controls.rules
 ** Abstract : component-handle:LoadControls rules.
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 HC  20190810 Initial version.
+** 002 DDF 20250424 Replaced file with artifact.
 */
  -->
 <!--
@@ -86,7 +87,7 @@
    <init-rules>
       <rule>sections = null</rule>
 
-      <rule>fHints = sprintf("%s.ext-hints", file)</rule>
+      <rule>fHints = sprintf("%s.ext-hints", artifact.getRelativePath())</rule>
       <rule>fileWorker.isFile(fHints, true)
          <action on="false">xmlHintsRoot = null</action>
 

=== modified file 'rules/annotations/naming.rules'
--- old/rules/annotations/naming.rules	2023-05-10 07:41:38 +0000
+++ new/rules/annotations/naming.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : naming.rules
 ** Abstract : handles adding class and package names
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ __________________________________Description__________________________________
 ** 001 SIY 20051103   @23186 Initial implementation.
@@ -66,6 +66,7 @@
 **                           @Override annotation.
 ** 022 OM  20230115          Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                           versions, based on node types rather on string paths.
+** 023 DDF 20250424          Replaced file with artifact.
 */
  -->
  
@@ -239,12 +240,12 @@
       </rule>
 
       <!-- calc the base Java class name (no pathing and no .java or .class) -->
-      <rule>convertedClasses.containsKey(file)
-         <action>classname = convertedClasses.get(file)</action>
+      <rule>convertedClasses.containsKey(artifact.getRelativePath())
+         <action>classname = convertedClasses.get(artifact.getRelativePath())</action>
          <action>classname = classname.substring(fullpkg.length() + 1)</action>
 
          <rule on="false">true
-            <rule>classname = execLib("convert_classname", file)</rule>
+            <rule>classname = execLib("convert_classname", artifact.getRelativePath())</rule>
             <rule>execLib("checkClassName")</rule>
             <rule>classnm = classname</rule>
       
@@ -255,14 +256,14 @@
                
                <action>idx = idx + 1</action>
             </while>
-            <rule>convertedClasses.put(file, classfile)</rule>
+            <rule>convertedClasses.put(artifact.getRelativePath(), classfile)</rule>
          </rule>
       </rule>
       
       <rule>putNote("classname", classname)</rule>
-      <rule>srv.isLegacyProxyClient(file)
+      <rule>srv.isLegacyProxyClient(artifact.getRelativePath())
          <variable name="proxyMethodName" type="java.lang.String" />
-         <action>proxyMethodName = file</action>
+         <action>proxyMethodName = artifact.getRelativePath()</action>
          <action>proxyMethodName = execLib("simple_filename", proxyMethodName)</action>
          <action>idx = proxyMethodName.lastIndexOf('.')</action>
          <rule>idx &lt; 0
@@ -281,7 +282,7 @@
       <!-- calc the name as it would be used in a Progress RUN statement 
            (which would include any relative pathing such that a PROPATH
            lookup will find the file) -->
-      <rule>relfile = file.substring(basepath.length() + 1)</rule>   
+      <rule>relfile = artifact.getRelativePath().substring(basepath.length() + 1)</rule>
       <!-- force to use '/' Linux style file separator for name_map.xml class mapping entries -->
       <rule>projectsep = "/"</rule>
       <rule>projectsep != fileSep
@@ -335,9 +336,9 @@
          <!-- store the qualified OO class or interface name at the root -->
          <action>ooname = text</action>
          <!-- this needs to be done first -->
-         <action>setConvertedClassName(file, ooname, fullpkg, classname)</action>
+         <action>setConvertedClassName(artifact.getRelativePath(), ooname, fullpkg, classname)</action>
          <!-- map the source file name to the converted class naming info -->
-         <action>clsdef = loadConvertedClass(file, true, this)</action>
+         <action>clsdef = loadConvertedClass(artifact.getRelativePath(), true, this)</action>
          
          <!-- save the converted default constructor name at the root node -->
          <rule>relativePath(this, prog.class_def, prog.kw_class, prog.symbol)

=== modified file 'rules/annotations/native_api.rules'
--- old/rules/annotations/native_api.rules	2020-03-24 18:36:19 +0000
+++ new/rules/annotations/native_api.rules	2025-05-09 07:59:16 +0000
@@ -4,7 +4,7 @@
 ** Module   : native_api.rules
 ** Abstract : Convert usages of Native API functions.
 **
-** Copyright (c) 2018-2020, Golden Code Development Corporation.
+** Copyright (c) 2018-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 HC  20181209 Initial version.
@@ -12,6 +12,7 @@
 **     HC  20190206 Fixed resolution of external library name.
 **     HC  20190305 Fixed conversion of memptr parameter type.
 ** 003 HC  20200324 Added missing memptr type mapping.
+** 004 DDF 20250424 Replaced file with artifact.
 */
  -->
 <!--
@@ -90,7 +91,7 @@
    <variable name="lnum"           type="java.lang.Long" />
 
    <init-rules>
-      <rule>fHints = sprintf("%s.ext-hints", file)</rule>
+      <rule>fHints = sprintf("%s.ext-hints", artifact.getRelativePath())</rule>
       <rule>fileWorker.isFile(fHints, true)
          <action on="false">xmlHintsRoot = null</action>
 

=== modified file 'rules/annotations/ocx_conversion.rules'
--- old/rules/annotations/ocx_conversion.rules	2024-01-15 13:58:34 +0000
+++ new/rules/annotations/ocx_conversion.rules	2025-05-09 07:59:16 +0000
@@ -4,7 +4,7 @@
 ** Module   : ocx_conversion.rules
 ** Abstract : Converts supported OCX objects to 4GL widgets.
 **
-** Copyright (c) 2018-2024, Golden Code Development Corporation.
+** Copyright (c) 2018-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 HC  20181127 Initial version.
@@ -94,6 +94,7 @@
 **                  versions, based on node types rather on string paths.
 ** 015 CA  20240115 Do not annotate a function or OO method call with 'wrap', instead annotate any literal
 **                  argument, as the parent call can be used as an argument itself.
+** 016 DDF 20250424 Replaced file with artifact.
 */
 -->
 
@@ -243,7 +244,7 @@
       <rule>dropProc = null</rule>
       <rule>dropNodes = create("java.util.HashSet")</rule>
 
-      <rule>fHints = sprintf("%s.ext-hints", file)</rule>
+      <rule>fHints = sprintf("%s.ext-hints", artifact.getRelativePath())</rule>
       <rule>fileWorker.isFile(fHints, true)
          <action on="false">xmlHintsRoot = null</action>
 

=== modified file 'rules/annotations/proxy_programs.xml'
--- old/rules/annotations/proxy_programs.xml	2023-05-10 07:41:38 +0000
+++ new/rules/annotations/proxy_programs.xml	2025-05-09 07:59:16 +0000
@@ -5,13 +5,14 @@
 ** Module   : proxy_programs.xml
 ** Abstract : mark legacy services proxy programs
 **
-** Copyright (c) 2022-2023, Golden Code Development Corporation.
+** Copyright (c) 2022-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description________________________________________
 ** 001 CA  20220513 Created initial version, extracted from legacy_services.rules.
 **     CA  20230125 Ignore FORWARD functions.
 ** 002 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 003 DDF 20250424 Replaced file with artifact.
 */
 -->
 
@@ -80,7 +81,7 @@
 
    <rule-set>
       <init-rules>
-         <rule>proxyClient = srv.isLegacyProxyClient(file)</rule>
+         <rule>proxyClient = srv.isLegacyProxyClient(artifact.getRelativePath())</rule>
       </init-rules>
       
       <walk-rules>>

=== modified file 'rules/annotations/record_field_expansion.rules'
--- old/rules/annotations/record_field_expansion.rules	2024-05-29 14:43:26 +0000
+++ new/rules/annotations/record_field_expansion.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : record_field_expansion.rules
 ** Abstract : convert implicit field refs into explicit versions
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 GES 20051103   @23224 Some UI and I/O language statements support syntax where a record
@@ -33,6 +33,7 @@
 **                           versions, based on node types rather on string paths.
 ** 013 AL2 20240529          Release the schema dictionary at the very end. Otherwise, it will cause NPE
 **                           in other rule sets that use the schema dictionary at the same time.
+** 014 DDF 20250423          Replaced file with artifact.
 */
 -->
 
@@ -191,7 +192,7 @@
           
          <!-- init the schema dictionary and ensure it has the source
               file specific temp-table data -->
-         <action>schemaw.initializeDictionary(file)</action>
+         <action>schemaw.initializeDictionary(artifact)</action>
          <action>sym.schemaDictionary = schemaw.dictionary</action>
          
          <!-- obtain the source nodes to expand, cache some key info and

=== modified file 'rules/annotations/return_statements.rules'
--- old/rules/annotations/return_statements.rules	2022-05-20 16:19:14 +0000
+++ new/rules/annotations/return_statements.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : return_statements.rules
 ** Abstract : differentiate between function style and procedure style returns
 **
-** Copyright (c) 2005-2022, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ __________________________________Description____________________________________
 ** 001 GES 20050919   @22808 Annotate return statements to note if executed from a function or 
@@ -22,6 +22,7 @@
 ** 007 CA  20200324          For RETURN ERROR, void OO methods are not functions.
 ** 008 CA  20220520          RETURN statements returning an unknown OO var must strong-type the 'object' 
 **                           instance with the function's/method's return qualified type.
+** 009 DDF 20250424          Replaced file with artifact.
 */
  -->
  
@@ -181,7 +182,7 @@
          <rule>btype == prog.trigger_block and descendant(prog.kw_error, 1)
             <action>
                printfln("## WARNING: %s: %d: RETURN ERROR invalid in TRIGGER", 
-                        file,
+                        artifact.getRelativePath(),
                         copy.line)
             </action>
          </rule>

=== modified file 'rules/callgraph/events.rules'
--- old/rules/callgraph/events.rules	2023-05-10 07:41:38 +0000
+++ new/rules/callgraph/events.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : events.rules
 ** Abstract : Process events and trigger calls.
 **
-** Copyright (c) 2017-2023, Golden Code Development Corporation.
+** Copyright (c) 2017-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 CA  20170522 Call graph analysis v3.
@@ -13,6 +13,8 @@
 **     OM  20220330 Moved database conversion artifacts to ${cvtpath} folder.
 ** 003 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 004 DDF 20250423 Replaced file with artifact.
+**     DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -122,7 +124,7 @@
    </func-library>
    
    <init-rules>
-      <rule>sw.initializeDictionary(file)</rule>
+      <rule>sw.initializeDictionary(artifact)</rule>
       <rule>schemaConfig = sw.getSchemaConfig()</rule>
       <rule>sdict = sw.getDictionary()</rule>
       <rule>dbname = null</rule>
@@ -151,7 +153,9 @@
             <rule>dbV == null
                <!-- add the vertex for the source file -->
                <action>
-                  schemafile = cgw.createFileResource("Schema File", prog.schema_file, dbRef.filename)
+                  schemafile = cgw.createFileResource("Schema File",
+                                                      prog.schema_file,
+                                                      dbRef.artifact.getRelativePath())
                </action>
                <action>cgw.property(schemafile, "srcfile", dbRef.getAnnotation("srcfile"))</action>
                

=== modified file 'rules/callgraph/function_calls.rules'
--- old/rules/callgraph/function_calls.rules	2023-05-10 06:23:45 +0000
+++ new/rules/callgraph/function_calls.rules	2025-05-09 07:59:16 +0000
@@ -6,7 +6,7 @@
 ** Abstract : processing of the function calls and (explicit and dynamic) for the
 **            generate_call_graph.xml
 **
-** Copyright (c) 2017-2023, Golden Code Development Corporation.
+** Copyright (c) 2017-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ ________________________________Description___________________________________
 ** 001 CA  20170517 Call graph analysis v3.
@@ -18,6 +18,7 @@
 **     CA  20180525 Callgraph improvements - changes in tx management, vertex caching and more.
 ** 004 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 005 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -184,7 +185,7 @@
          <action>callSiteKey = prog.function_call</action>
 
          <action>
-            targetV = execLib("resolve_function_vertex", target, this.filename)
+            targetV = execLib("resolve_function_vertex", target, this.artifact.getRelativePath())
          </action>
 
          <action>execLib("link_to_target_vertex", container, callSite, callSiteKey, targetV)</action>
@@ -223,7 +224,7 @@
          </rule>
 
          <!-- check for ADM/ADM2 related calls -->
-         <rule>this.filename.endsWith("adm2/containr.p")
+         <rule>this.artifact.getRelativePath().endsWith("adm2/containr.p")
             <action>procV = cgw.findParentAstVertex(container, prog.internal_procedure)</action>
             <rule>procV != null
                <rule>cgw.property(procV, "procedure").equals("assignpageproperty")

=== modified file 'rules/callgraph/generate_call_graph.xml'
--- old/rules/callgraph/generate_call_graph.xml	2020-04-13 11:17:30 +0000
+++ new/rules/callgraph/generate_call_graph.xml	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : generate_call_graph.xml
 ** Abstract : build call graph and unused files list.        
 **
-** Copyright (c) 2005-2020, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description______________________________
 ** 001 SIY 20050719   @21724 Generate call graph and dead files list.
@@ -22,6 +22,7 @@
 **     CA  20180525          Callgraph improvements - changes in tx management, vertex caching and 
 **                           more.
 ** 007 CA  20200412          Added incremental conversion support.
+** 008 DDF 20250423          Replaced filename with artifact.
 */
 -->
 
@@ -112,8 +113,10 @@
       <variable name="hintCounters" type="java.util.Map"/>
       
       <init-rules>
-         <rule>thisAdmProgram = evalLib("is_adm_program", this.filename)</rule>
-         <rule>procfile = cgw.findUniqueNode("filename", cgw.prepareFilename(this.filename))</rule>
+         <rule>thisAdmProgram = evalLib("is_adm_program", this.artifact.getRelativePath())</rule>
+         <rule>
+            procfile = cgw.findUniqueNode("filename", cgw.prepareFilename(this.artifact.getRelativePath()))
+         </rule>
          <rule>cgw.markReachable(procfile)</rule>
          <rule>cgw.markLive(procfile)</rule>
          <rule>app_entry_point = cgw.property(procfile, "app-entry-point")</rule>

=== modified file 'rules/callgraph/generate_call_graph_post.xml'
--- old/rules/callgraph/generate_call_graph_post.xml	2018-05-25 14:12:52 +0000
+++ new/rules/callgraph/generate_call_graph_post.xml	2025-05-09 07:59:16 +0000
@@ -5,12 +5,13 @@
 ** Module   : generate_call_graph_post.xml
 ** Abstract : rules executed once the initial graph is completed.
 **
-** Copyright (c) 2017-2018, Golden Code Development Corporation.
+** Copyright (c) 2017-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ ________________________________Description___________________________________
 ** 001 CA  20170604 Call graph analysis v3.
 ** 002 CA  20180328 Performance optimizations.
 ** 003 CA  20180525 Callgraph improvements - changes in tx management, vertex caching and more.
+** 004 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -95,7 +96,9 @@
       <variable name="hintCounters" type="java.util.Map"/>
       
       <init-rules>
-         <rule>procfile = cgw.findUniqueNode("filename", cgw.prepareFilename(this.filename))</rule>
+         <rule>
+            procfile = cgw.findUniqueNode("filename", cgw.prepareFilename(this.artifact.getRelativePath()))
+         </rule>
          <rule>extproc = cgw.findNodeById(prog.external_procedure, this.id)</rule>
          <rule>hintCounters = create("java.util.HashMap")</rule>
 

=== modified file 'rules/callgraph/list_ambiguous.rules'
--- old/rules/callgraph/list_ambiguous.rules	2017-06-02 21:16:11 +0000
+++ new/rules/callgraph/list_ambiguous.rules	2025-05-09 07:59:16 +0000
@@ -5,12 +5,13 @@
 ** Module   : list_ambiguous.rules
 ** Abstract : Tool to list all ambiguous call sites.
 **
-** Copyright (c) 2014-2017, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 CA  20140313 Created initial version.
 ** 002 CA  20140403 Fixed filename output on windows OS.
 ** 003 GES 20170426 Call graph analysis v3.
+** 004 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -85,7 +86,7 @@
       <rule>this.parent == null
          <action>first = true</action>
 
-         <action>fhname = sprintf("%s.hints", this.filename)</action>
+         <action>fhname = sprintf("%s.hints", this.artifact.getRelativePath())</action>
          <rule>createEmptyHints
             <action>io.deleteFile(fhname, false)</action>
          </rule>
@@ -100,7 +101,9 @@
             <action>iter = cgw.edgeIterator(out, "ambiguous")</action>
             
             <rule>first and iter.hasNext()
-               <action>fprintf(fname, "\nFilename: %s\n", fixupFileName(this.filename))</action>
+               <action>
+                  fprintf(fname, "\nFilename: %s\n", fixupFileName(this.artifact.getRelativePath()))
+               </action>
                <action>first = false</action>
 
                <rule>createEmptyHints

=== modified file 'rules/callgraph/list_dead_files.rules'
--- old/rules/callgraph/list_dead_files.rules	2017-05-30 23:15:42 +0000
+++ new/rules/callgraph/list_dead_files.rules	2025-05-09 07:59:16 +0000
@@ -5,13 +5,14 @@
 ** Module   : list_dead_files.rules
 ** Abstract : Tool to list all dead files.
 **
-** Copyright (c) 2014-2017, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 CA  20140313 Created initial version.
 ** 002 CA  20140403 Fixed filename output on windows OS.
 ** 003 CA  20140513 Fixed a problem when determining if an include file is dead or not.
 ** 004 GES 20170501 Call graph v3.
+** 005 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -87,7 +88,7 @@
             #(java.lang.Boolean) cgw.property(out, "app-entry-point") == false
          <action>iter = cgw.vertexTraversalInE(out)</action>
          <rule>!iter.hasNext()
-            <action>fprintf(fname, "%s\n", fixupFileName(this.filename))</action>
+            <action>fprintf(fname, "%s\n", fixupFileName(this.artifact.getRelativePath()))</action>
 
             <!-- collect all the include nodes in the include sub-graph for this dead file -->
             <action>iter = cgw.traversalIterator(out, "includes")</action>

=== modified file 'rules/callgraph/list_dependencies.rules'
--- old/rules/callgraph/list_dependencies.rules	2017-05-13 08:16:53 +0000
+++ new/rules/callgraph/list_dependencies.rules	2025-05-09 07:59:16 +0000
@@ -5,12 +5,13 @@
 ** Module   : list_dependencies.rules
 ** Abstract : List the direct dependencies for all external procedures.
 **
-** Copyright (c) 2014-2017, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 CA  20140313 Created initial version.
 ** 002 CA  20140403 Fixed filename output on windows OS.
 ** 003 GES 20170426 Call graph analysis v3.
+** 004 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -99,7 +100,9 @@
             </action>
             
             <rule>first and iter.hasNext()
-               <action>fprintf(fname, "\nFilename: %s\n", fixupFileName(this.filename))</action>
+               <action>
+                  fprintf(fname, "\nFilename: %s\n", fixupFileName(this.artifact.getRelativePath()))
+               </action>
                <action>first = false</action>
             </rule>
             

=== modified file 'rules/callgraph/list_external_targets.rules'
--- old/rules/callgraph/list_external_targets.rules	2017-05-13 08:16:53 +0000
+++ new/rules/callgraph/list_external_targets.rules	2025-05-09 07:59:16 +0000
@@ -5,13 +5,14 @@
 ** Module   : list_external_targets.rules
 ** Abstract : Tool to list all external dependencies.
 **
-** Copyright (c) 2014-2017, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 CA  20140313 Created initial version.  List all nodes marked as "external" and the location
 **                  where they are called.
 ** 002 CA  20140403 Fixed filename output on windows OS.
 ** 003 GES 20170426 Call graph analysis v3.
+** 004 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -101,7 +102,9 @@
       
                <rule>cgw.property(in, "external") != null
                   <rule>first
-                     <action>fprintf(fname, "\nFilename: %s\n", fixupFileName(this.filename))</action>
+                     <action>
+                        fprintf(fname, "\nFilename: %s\n", fixupFileName(this.artifact.getRelativePath()))
+                     </action>
                      <action>first = false</action>
                   </rule>
       

=== modified file 'rules/callgraph/list_missing.rules'
--- old/rules/callgraph/list_missing.rules	2017-05-13 08:16:53 +0000
+++ new/rules/callgraph/list_missing.rules	2025-05-09 07:59:16 +0000
@@ -5,12 +5,13 @@
 ** Module   : list_missing.rules
 ** Abstract : Tool to list all missing 4GL programs and include files.
 **
-** Copyright (c) 2014-2017, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 CA  20140313 Created initial version.
 ** 002 CA  20140403 Fixed filename output on windows OS.
 ** 003 GES 20170426 Call graph analysis v3.
+** 004 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -103,7 +104,7 @@
                <rule>inType == prog.missing
                   <rule>first
                      <action>
-                        fprintf(fname, "\nFilename: %s\n", fixupFileName(this.filename))
+                        fprintf(fname, "\nFilename: %s\n", fixupFileName(this.artifact.getRelativePath()))
                      </action>
                      <action>first = false</action>
                   </rule>

=== modified file 'rules/callgraph/load_code_set.xml'
--- old/rules/callgraph/load_code_set.xml	2023-05-10 07:41:38 +0000
+++ new/rules/callgraph/load_code_set.xml	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : load_code_set.xml
 ** Abstract : Load all the legacy files for the entire code set.
 **
-** Copyright (c) 2014-2023, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 CA  20140306 Created initial version.  Loads all the legacy files (4GL programs and
@@ -18,6 +18,7 @@
 ** 006 CA  20200428 Legacy DEFINE ENUM conversion support.
 ** 007 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 008 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -146,7 +147,7 @@
       <init-rules>
          <!-- check if the procedure file node exists -->
          <rule>
-            procfile = cgw.findUniqueNode("os-filename", this.filename)
+            procfile = cgw.findUniqueNode("os-filename", this.artifact.getRelativePath())
          </rule>
 
          <rule>procfile == null
@@ -154,10 +155,10 @@
             <action>
                procfile = cgw.createFileResource("Procedure File",
                                                  prog.procedure_file,
-                                                 this.filename)
+                                                 this.artifact)
             </action>
             
-            <rule>evalLib("is_adm_program", this.filename)
+            <rule>evalLib("is_adm_program", this.artifact.getRelativePath())
                <action>cgw.property(procfile, "adm-program", true)</action>
             </rule>
          </rule>
@@ -171,7 +172,7 @@
                extproc = cgw.createAstNode(procfile, this, prog.external_procedure, true)
             </action>
             <action>cgw.property(extproc, "node-key", "external-procedure")</action>
-            <action>cgw.property(extproc, "external-procedure", this.filename)</action>
+            <action>cgw.property(extproc, "external-procedure", this.artifact.getRelativePath())</action>
 
             <!--  this is a new AST, load its include graph -->
             <action>cgw.createIncludeGraph(this)</action>

=== modified file 'rules/callgraph/load_code_set_post.xml'
--- old/rules/callgraph/load_code_set_post.xml	2023-05-10 06:23:45 +0000
+++ new/rules/callgraph/load_code_set_post.xml	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : load_code_set_post.xml
 ** Abstract : Rules to be executed after the code-set was loaded.
 **
-** Copyright (c) 2017-2023, Golden Code Development Corporation.
+** Copyright (c) 2017-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 CA  20170517 Call graph analysis v3.
@@ -23,6 +23,7 @@
 ** 005 CA  20191015 Used is_builtin_cls to check if a class ref is built-in.
 ** 006 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 007 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -165,7 +166,9 @@
       <init-rules>
          <rule>hintCounters = create("java.util.HashMap")</rule>
 
-         <rule>procfile = cgw.findUniqueNode("filename", cgw.prepareFilename(this.filename))</rule>
+         <rule>
+            procfile = cgw.findUniqueNode("filename", cgw.prepareFilename(this.artifact.getRelativePath()))
+         </rule>
          <rule>extproc = cgw.findNodeById(prog.external_procedure, this.id)</rule>
          <rule>container = extproc</rule>
 

=== modified file 'rules/callgraph/load_schema_triggers.xml'
--- old/rules/callgraph/load_schema_triggers.xml	2019-10-07 05:56:57 +0000
+++ new/rules/callgraph/load_schema_triggers.xml	2025-05-09 07:59:16 +0000
@@ -6,7 +6,7 @@
 ** Abstract : Loads all the tables with defined schema-triggers and links them with the
 **            target external program.
 **
-** Copyright (c) 2014-2018, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 CA  20140313 Created initial version.
@@ -22,6 +22,8 @@
 **                  with NO_PROCEDURE_SPECIFIED_AT_TRIGGER_DEFINITION.
 ** 006 CA  20191007 Fixed some issues after changes in the .schema AST (nodes were moved to 
 **                  annotations).
+** 007 DDF 20250423 Replaced file with artifact.
+**     DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -105,7 +107,7 @@
       <variable name="tIter"       type="java.util.Iterator" />
       
       <init-rules>
-         <rule>sw.initializeDictionary(file)</rule>
+         <rule>sw.initializeDictionary(artifact)</rule>
          <rule>sw.loadNonDefaults()</rule>
          <rule>sdict = sw.getDictionary()</rule>
 
@@ -122,7 +124,7 @@
             <action>
                schemafile = cgw.createFileResource("Schema File",
                                                    prog.schema_file,
-                                                   this.filename)
+                                                   this.artifact)
             </action>
             <action>cgw.property(schemafile, "srcfile", getNoteString("srcfile"))</action>
             

=== modified file 'rules/callgraph/run_statements.rules'
--- old/rules/callgraph/run_statements.rules	2023-05-10 06:23:45 +0000
+++ new/rules/callgraph/run_statements.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : run_statements.rules
 ** Abstract : processing of the run statements for the generate_call_graph.xml
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 SIY 20050719   @21730 Extracted from the generate_call_graph.xml.
@@ -25,6 +25,7 @@
 **                           more.
 ** 008 OM  20230115          Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                           versions, based on node types rather on string paths.
+** 009 DDF 20250423          Replaced filename with artifact.
 */
 -->
 
@@ -357,7 +358,9 @@
                <action>target = chRef.text.toLowerCase()</action>
                <action>callSiteKey = prog.run_int_proc</action>
                <action>
-                  targetV = execLib("resolve_internal_procedure_vertex", target, this.filename)
+                  targetV = execLib("resolve_internal_procedure_vertex",
+                                    target,
+                                    this.artifact.getRelativePath())
                </action>
 
                <rule>targetV != null
@@ -401,7 +404,7 @@
                     fetchfirst or fetchlast -->
                <action>procV = cgw.findParentAstVertex(container, prog.function)</action>
                <rule>procV != null and cgw.property(procV, "function").equals("openquery")
-                  <rule>this.filename.endsWith("adm2/query.p") and 
+                  <rule>this.artifact.getRelativePath().endsWith("adm2/query.p") and
                         downPath(this, prog.kw_value, prog.expression, prog.plus, prog.string)
                      <action>
                         target = this.firstChild.firstChild.firstChild.firstChild.text
@@ -415,7 +418,7 @@
                   </rule>
                </rule>
 
-               <rule>this.filename.endsWith("adm2/data.p")
+               <rule>this.artifact.getRelativePath().endsWith("adm2/data.p")
                   <action>
                      procV = cgw.findParentAstVertex(container, prog.internal_procedure)
                   </action>
@@ -467,7 +470,7 @@
                   </rule>
                </rule>
 
-               <rule>this.filename.endsWith("adm2/containr.p")
+               <rule>this.artifact.getRelativePath().endsWith("adm2/containr.p")
                   <action>
                      procV = cgw.findParentAstVertex(container, prog.internal_procedure)
                   </action>
@@ -490,7 +493,7 @@
                   </rule>
                </rule>
 
-               <rule>this.filename.endsWith("adm2/smart.p")
+               <rule>this.artifact.getRelativePath().endsWith("adm2/smart.p")
                   <action>
                      procV = cgw.findParentAstVertex(container, prog.internal_procedure)
                   </action>
@@ -589,7 +592,9 @@
 
             <action>callSiteKey = prog.run_int_proc</action>
             <action>
-               targetV = execLib("resolve_internal_procedure_vertex", target, this.filename)
+               targetV = execLib("resolve_internal_procedure_vertex",
+                                 target,
+                                 this.artifact.getRelativePath())
             </action>
 
             <rule>targetV != null

=== modified file 'rules/callgraph/verify_schema_triggers.rules'
--- old/rules/callgraph/verify_schema_triggers.rules	2017-05-13 08:16:53 +0000
+++ new/rules/callgraph/verify_schema_triggers.rules	2025-05-09 07:59:16 +0000
@@ -6,13 +6,14 @@
 ** Abstract : Tool to verify problems in schema trigger definitions or external programs defined
 **            as a schema trigger.
 **
-** Copyright (c) 2014-2017, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 CA  20140313 Created initial version.
 ** 002 CA  20140403 Fixed filename output on windows OS.
 ** 003 CA  20140715 Fixed typo related to node type "trigger-type".
 ** 004 GES 20170426 Call graph analysis v3.
+** 005 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -81,7 +82,7 @@
 
          <rule>first
             <action>
-               fprintf(fname, sprintf("\nFilename: %s\n", fixupFileName(this.filename)))
+               fprintf(fname, sprintf("\nFilename: %s\n", fixupFileName(this.artifact.getRelativePath())))
             </action>
             <action>first = false</action>
          </rule>

=== modified file 'rules/convert/frame_generator.xml'
--- old/rules/convert/frame_generator.xml	2025-02-18 08:15:09 +0000
+++ new/rules/convert/frame_generator.xml	2025-05-09 07:59:16 +0000
@@ -420,6 +420,8 @@
 ** 228 EM  20241002          Suppressed the display of database labels in FORM if NO-LABELS is present.
 **     EM  20241010          Suppressed the display of database labels in FRAME if NO-LABELS is present.
 ** 229 AOG 20250217          Changed the way filename is extracted for image phrases / button images.
+** 230 DDF 20250423          Refactor usage of filenames and replace them with artifacts.
+**     DDF 20250424          Replaced file with artifact.
 */
 -->
 
@@ -520,6 +522,7 @@
    <worker class="com.goldencode.p2j.pattern.TemplateWorker"             namespace="tw" />
    <worker class="com.goldencode.p2j.convert.ExpressionConversionWorker" namespace="ecw" />
    <worker class="com.goldencode.p2j.convert.I18nWorker"                 namespace="trw" />
+   <worker class="com.goldencode.artifacts.ArtifactWorker"               namespace="art" />
    
    <!-- expression libraries -->
    <include name="common-progress" />
@@ -1043,21 +1046,21 @@
       <!-- generate Java AST -->
       <function name="init_jast">
          <parameter name="fnode"  type="com.goldencode.ast.Aast" />
-         <return name="fname"     type="java.lang.String" />
+         <return name="fartifact" type="com.goldencode.artifacts.Artifact" />
          <variable name="cname"   type="java.lang.String" />
          <variable name="ext"     type="java.lang.String" />
          <variable name="rule"    type="java.lang.String" />
          <variable name="pkgname" type="java.lang.String" />
          <variable name="acc"     type="java.lang.String" />
          <variable name="id"      init="javaRootId" />
-         
+
          <rule>true
             <action>cname = execLib("gen_cname", fnode, true)</action>
-            <action>fname = execLib("gen_fname", cname)</action>
-            
+            <action>fartifact = execLib("gen_fartifact", cname)</action>
+
             <action>pkgname = #(java.lang.String) fnode.getAnnotation("pkgname")</action>
-            
-            <action>createJavaFile(fname, true)</action>
+
+            <action>createJavaFile(fartifact, true)</action>
             <action>createJavaAst(java.kw_package, pkgname, javaRootId)</action>
             
             <action>createImport("com.goldencode.p2j.util.*")</action>
@@ -2970,13 +2973,13 @@
             <rule>ref.type == prog.kw_x_of or ref.type == prog.kw_col_of
                <action>
                   printfln("## Error:: unsupported option %s/%s in %s %d",
-                           ref.parent, ref, file, line)
+                           ref.parent, ref, artifact.getRelativePath(), line)
                </action>
             </rule>
             <rule>ref.type == prog.kw_y_of or ref.type == prog.kw_row_of
                <action>
                   printfln("## Error:: unsupported option %s/%s in %s %d",
-                           ref.parent, ref, file, line)
+                           ref.parent, ref, artifact.getRelativePath(), line)
                </action>
             </rule>
 
@@ -3417,13 +3420,13 @@
             <rule>ref.type == prog.kw_x_of or ref.type == prog.kw_col_of
                <action>
                   printfln("## Error:: unsupported option %s/%s in %s %d",
-                           ref.parent, ref, file, line)
+                           ref.parent, ref, artifact.getRelativePath(), line)
                </action>
             </rule>
             <rule>ref.type == prog.kw_y_of or ref.type == prog.kw_row_of
                <action>
                   printfln("## Error:: unsupported option %s/%s in %s %d",
-                           ref.parent, ref, file, line)
+                           ref.parent, ref, artifact.getRelativePath(), line)
                </action>
             </rule>
 
@@ -5906,7 +5909,6 @@
       <function name="gen_interface">
          <parameter name="frame"  type="java.util.Map" />
          <variable name="fnode"   type="com.goldencode.ast.Aast" />
-         <variable name="fname"   type="java.lang.String" />
          <variable name="cname"   type="java.lang.String" />
          <variable name="pkgname" type="java.lang.String" />
          <variable name="map"     type="java.util.Map" />
@@ -5914,14 +5916,15 @@
          <variable name="node"    type="com.goldencode.ast.Aast" />
          <variable name="inn"     type="com.goldencode.ast.Aast" />
          <variable name="lastId"  type="java.lang.Long" />
+         <variable name="fartifact"  type="com.goldencode.artifacts.Artifact" />
          
          <rule>true
             <action>fnode = execLib("get_fnode", frame)</action>
             <action>cname = #(java.lang.String) fnode.getAnnotation("fr_interface")</action>
-            <action>fname = #(java.lang.String) execLib("gen_fname", cname)</action>
+            <action>fartifact = execLib("gen_fartifact", cname)</action>
             <action>pkgname = #(java.lang.String) fnode.getAnnotation("pkg_int")</action>
             
-            <action>createJavaFile(fname, true)</action>
+            <action>createJavaFile(fartifact, true)</action>
             <action>createJavaAst(java.kw_package, pkgname, javaRootId)</action>
             <action>createImport("com.goldencode.p2j.util.*")</action>
             <action>createImport("com.goldencode.p2j.ui.*")</action>
@@ -5943,7 +5946,7 @@
             <action>execLib("gen_method_signatures", map)</action>
             
             <!-- persist JAST -->
-            <rule>persistJavaFile(fname)</rule>
+            <rule>persistJavaFile(fartifact)</rule>
          </rule>
       </function>
       
@@ -5951,17 +5954,17 @@
       <function name="gen_code">
          <parameter name="frame"     type="java.util.Map" />
          <variable name="fnode"      type="com.goldencode.ast.Aast" />
-         <variable name="fname"      type="java.lang.String" />
+         <variable name="fartifact"  type="com.goldencode.artifacts.Artifact" />
          <variable name="map"        type="java.util.Map" />
          <variable name="isText"     init="false"/>
          <variable name="isStreamIO" init="false"/>
          <variable name="node"       type="com.goldencode.ast.Aast" />
          <variable name="inn"        type="com.goldencode.ast.Aast" />
          <variable name="lastId"     type="java.lang.Long" />
-         
+
          <rule>true
             <action>fnode = execLib("get_fnode", frame)</action>
-            <action>fname = execLib("init_jast", fnode)</action>
+            <action>fartifact = execLib("init_jast", fnode)</action>
 
             <!-- i18n -->
             <rule>i18nEnable
@@ -6019,7 +6022,7 @@
                execLib("gen_add_widget", map, !frame.get(frameExtra).equals(""), node.id)
             </action>
             <!-- persist JAST -->
-            <rule>persistJavaFile(fname)</rule>
+            <rule>persistJavaFile(fartifact)</rule>
          </rule>
       </function>
       
@@ -6164,20 +6167,6 @@
          </rule>
       </function>
       
-      <!-- generate frame class file name (no extension) -->
-      <function name="gen_fname">
-         <parameter name="cname" type="java.lang.String" />
-         <return name="fname"    init="''" />
-         
-         <rule>true
-            <action>
-               fname = file.substring(0, file.lastIndexOf(fileSep))
-            </action>
-            
-            <action>fname = sprintf("%s%s%s", fname, fileSep, cname)</action>
-         </rule>
-      </function>
-      
       <!-- allocate new frame instance -->
       <function name="new_frame">
          <return name="frame" type="java.util.Map" />

=== modified file 'rules/convert/menu_generator.xml'
--- old/rules/convert/menu_generator.xml	2023-05-10 07:41:38 +0000
+++ new/rules/convert/menu_generator.xml	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : menu_generator.xml
 ** Abstract : Generates MENU code.
 **
-** Copyright (c) 2015-2023, Golden Code Development Corporation.
+** Copyright (c) 2015-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 VIG 20141101 Initial version.
@@ -29,6 +29,7 @@
 ** 014 HC  20211001 Implementation of i18n support.
 ** 015 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 016 DDF 20250423 Replace filename usage with artifacts.
 */
 -->
 
@@ -93,6 +94,7 @@
    <worker class="com.goldencode.p2j.convert.ConverterHelper"            namespace="help" />
    <worker class="com.goldencode.p2j.pattern.TemplateWorker"             namespace="tw" />
    <worker class="com.goldencode.p2j.convert.ExpressionConversionWorker" namespace="ecw" />
+   <worker class="com.goldencode.artifacts.ArtifactWorker"               namespace="art" />
 
 
    <!-- expression libraries -->
@@ -111,7 +113,7 @@
    <variable name="cname"        type="java.lang.String" /> 
    <variable name="location"     type="java.lang.Long" />
    <variable name="menuConfig"   type="java.util.Map" />
-   <variable name="lastFile"     type="java.lang.String" />
+   <variable name="lastArtifact" type="com.goldencode.artifacts.Artifact" />
 
    <variable name="setUpLastId"  type="com.goldencode.ast.Aast" />
    <variable name="label"        type="java.lang.String" />
@@ -154,7 +156,7 @@
         
          <rule>uiStrings = createStringSet('uiStrings')</rule>
 
-         <rule>lastFile = null</rule>
+         <rule>lastArtifact = null</rule>
 
          <rule>baseClass = getNoteString("classname")</rule>
          <rule>baseClassPkgname = getNoteString("pkgname")</rule>
@@ -468,9 +470,9 @@
      
       <post-rules> 
          <!-- persist the last definition file -->
-         <rule>lastFile != null
+         <rule>lastArtifact != null
             <action>execLib("gen_add_widgets", pnames, wtypes)</action>
-            <action>persistJavaFile(lastFile)</action>
+            <action>persistJavaFile(lastArtifact)</action>
          </rule>
       </post-rules>
    </rule-set>
@@ -610,7 +612,7 @@
                <action on="false">clspfx = "SubMenu"</action>
             </rule>
  
-            <action>lastFile = execLib("init_jast", this, clspfx)</action>
+            <action>lastArtifact = execLib("init_jast", this, clspfx)</action>
 
             <!-- i18n -->
             <rule>i18nEnable
@@ -650,7 +652,7 @@
       <function name="init_jast">
          <parameter name="mnode"     type="com.goldencode.ast.Aast" />
          <parameter name="clspfx"   type="java.lang.String" />
-         <return name="fname"        type="java.lang.String" />
+         <return name="fartifact"    type="com.goldencode.artifacts.Artifact" />
 
          <variable name="cname"     type="java.lang.String" />
          <variable name="ext"       type="java.lang.String" />
@@ -659,7 +661,7 @@
          <variable name="id"        init="javaRootId" />
          <variable name="classname" type="java.lang.String" />
          <variable name="extends"   type="java.lang.String" />
- 
+
          <rule>true
             <action>
                classname = sprintf("%s%s%s", 
@@ -670,18 +672,18 @@
              
             <action>extends = sprintf("%s%s", clspfx, "Definition")</action>
             <action>cname = classname</action>
-            <action>fname = execLib("gen_fname", cname)</action>
+            <action>fartifact = execLib("gen_fartifact", cname)</action>
 
-            <action>printfln("Creating separate file: %s %s", cname, fname)</action>
+            <action>printfln("Creating separate file: %s %s", cname, fartifact.getRelativePath())</action>
             
-            <rule>lastFile != null
+            <rule>lastArtifact != null
                <action>execLib("gen_add_widgets", pnames, wtypes)</action>
-               <action>persistJavaFile(lastFile)</action>
+               <action>persistJavaFile(lastArtifact)</action>
             </rule>
 
-            <action>createJavaFile(fname, true)</action>
+            <action>createJavaFile(fartifact, true)</action>
 
-            <action>lastFile = fname</action>           
+            <action>lastArtifact = fartifact</action>
 
             <action>pkgname = #(java.lang.String) execLib("gen_pkg")</action>
 
@@ -703,20 +705,6 @@
          </rule>
       </function>
 
-      <!-- generate menu class file name (no extension) -->
-      <function name="gen_fname">
-         <parameter name="cname" type="java.lang.String" />
-         <return name="fname"    init="''" />
-
-         <rule>true
-            <action>
-               fname = file.substring(0, file.lastIndexOf(fileSep))
-            </action>
-
-            <action>fname = sprintf("%s%s%s", fname, fileSep, cname)</action>
-         </rule>
-      </function>
-
       <!-- allocate new menu instance -->
       <function name="new_menu">
          <return name="menu" type="java.util.Map" />

=== modified file 'rules/convert/method_definitions.rules'
--- old/rules/convert/method_definitions.rules	2024-03-14 19:16:31 +0000
+++ new/rules/convert/method_definitions.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : method_definitions.rules
 ** Abstract : converts Progress method definitions into Java method defs
 **
-** Copyright (c) 2018-2024, Golden Code Development Corporation.
+** Copyright (c) 2018-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 GES 20181213 First version.
@@ -45,6 +45,7 @@
 **                  entry, as it is reported by PROGRAM-NAME and error callstack.
 ** 014 CA  20240314 A property getter with only a "return <literal>." body will convert to a Java return which
 **                  will wrap the literal to a BDT.
+** 015 DDF 20250423 Replace usage of file with artifact.
 */
 -->
 
@@ -149,7 +150,7 @@
    </func-library>
    
    <init-rules>
-      <rule>schema.initializeDictionary(file)</rule>
+      <rule>schema.initializeDictionary(artifact)</rule>
 
       <rule>imptype = false</rule>
       <rule>rootref = this</rule>

=== modified file 'rules/convert/proxy_programs.xml'
--- old/rules/convert/proxy_programs.xml	2023-05-10 07:41:38 +0000
+++ new/rules/convert/proxy_programs.xml	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : proxy_programs.xml
 ** Abstract : conversion related to legacy proxy client code, for a specific AppObject (xpxg file)
 **
-** Copyright (c) 2020-2023, Golden Code Development Corporation.
+** Copyright (c) 2020-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description________________________________________
 ** 001 CA  20201007 Created initial version.
@@ -31,6 +31,8 @@
 **     CA  20230125 Parameter mode defaults to INPUT in case of functions.
 ** 002 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 003 DDF 20250423 Replace usage of filenames with artifacts.
+**     DDF 20250424 Replaced file with artifact.
 */
 -->
 
@@ -120,6 +122,7 @@
       <variable name="fldAnchor"  type="com.goldencode.ast.Aast" />
       <variable name="index"      type="com.goldencode.p2j.persist.P2JIndex" />
       <variable name="indexComp"  type="com.goldencode.p2j.persist.P2JIndexComponent" />
+      <variable name="fartifact"  type="com.goldencode.artifacts.Artifact" />
       <variable name="indexes"    type="java.lang.String" />
       <variable name="argname"    type="java.lang.String" />
       <variable name="argmode"    type="java.lang.String" />
@@ -162,30 +165,39 @@
             <return name="appObjectRoot" type="com.goldencode.p2j.uast.JavaAst" />
             
             <variable name="appObjectFile" type="java.lang.String" />
+            <variable name="appObjectArtifact" type="com.goldencode.artifacts.Artifact" />
             
             <rule>
                appObjectFile = sprintf("%s%s%s.proxy.jast", 
-                                       srv.getProxyPackageFolder(file), 
+                                       srv.getProxyPackageFolder(artifact.getRelativePath()),
                                        fileSep, 
                                        appObjectName)
             </rule>
-            <rule>appObjectRoot = createJavaFile(appObjectFile)</rule>
-            <rule>appObjectRoot.putAnnotation("filename", appObjectFile)</rule>
+            <rule>appObjectArtifact = art.createArtifact(appObjectFile)</rule>
+            <rule>appObjectRoot = createJavaFile(appObjectArtifact)</rule>
+            <rule>appObjectRoot.putAnnotation("filename", appObjectArtifact.getRelativePath())</rule>
    
             <rule>
                tw.graft("class_proxy_client_definition", null, appObjectRoot, 
                         "classname", appObjectName,
-                        "pkgname", srv.getProxyPackage(file),
-                        "trackChanges", sprintf("%s", srv.isProxyTrackChanges(file)))
+                        "pkgname", srv.getProxyPackage(artifact),
+                        "trackChanges", sprintf("%s", srv.isProxyTrackChanges(artifact.getRelativePath())))
             </rule>
             <rule>
-               srv.registerAppObjectJast(sprintf("%s.%s", srv.getProxyPackage(file), appObject), appObjectRoot)
+               srv.registerAppObjectJast(sprintf("%s.%s",
+                                                 srv.getProxyPackage(artifact.getRelativePath()), appObject),
+                                                 appObjectRoot)
             </rule>
             <rule>ref = createJavaAst(java.doc_comment, "", appObjectRoot, 0)</rule>
             <rule>ref = createJavaAst(java.doc_paragraph, "", ref)</rule>
    
             <!-- add package now -->
-            <rule>createJavaAst(java.kw_package, srv.getProxyPackage(file), appObjectRoot, 0)</rule>
+            <rule>
+               createJavaAst(java.kw_package,
+                             srv.getProxyPackage(artifact.getRelativePath()),
+                             appObjectRoot,
+                             0)
+            </rule>
    
             <rule>
                createJavaAst(java.doc_textrun, 
@@ -265,7 +277,12 @@
             </rule>
    
             <rule>jtype == null
-               <action>printfln("WARNING: type %s is not supported, at %s:%s", atype, file, this.id)</action>
+               <action>
+                  printfln("WARNING: type %s is not supported, at %s:%s",
+                           atype,
+                           artifact.getRelativePath(),
+                           this.id)
+               </action>
             </rule>
    
             <rule>extent != null and extent != -2
@@ -275,7 +292,7 @@
       </func-library>
    
       <init-rules>
-         <rule>schema.initializeDictionary(file)</rule>
+         <rule>schema.initializeDictionary(artifact)</rule>
          <rule>legacyNames = getConfigParameter("proxy-legacy-names", false)</rule>
       
          <rule>deleteDictionary("proxy")</rule>
@@ -284,18 +301,20 @@
          <rule>appObjectJast = null</rule>
          
          <rule>getNoteBoolean("proxy-client-program")
-            <action>appObject = srv.getProxyAppObjectName(file)</action>
+            <action>appObject = srv.getProxyAppObjectName(artifact.getRelativePath())</action>
             <action>proxyMethodName = getNoteString("proxy_method_name")</action>
    
             <!-- create the AppObject JAST -->
             <action>
-               appObjectJast = srv.getAppObjectJast(sprintf("%s.%s", srv.getProxyPackage(file), appObject))
+               appObjectJast = srv.getAppObjectJast(sprintf("%s.%s",
+                                                            srv.getProxyPackage(artifact.getRelativePath()),
+                                                            appObject))
             </action>
             <rule>appObjectJast == null
                <action>
                   appObjectJast = execLib("create_appObject_class", 
                                           appObject, 
-                                          srv.getProxyLegacyAppObjectName(file))
+                                          srv.getProxyLegacyAppObjectName(artifact.getRelativePath()))
                </action>
             </rule>
    
@@ -305,9 +324,9 @@
             <action>fldAnchor = ref.getImmediateChild(java.cs_constants, null)</action>
             
             <rule>
-               persistent = srv.isProxyPersistentProgram(file) or
-                            srv.isProxySingletonProgram(file)  or
-                            srv.isProxySingleRunProgram(file)
+               persistent = srv.isProxyPersistentProgram(artifact.getRelativePath()) or
+                            srv.isProxySingletonProgram(artifact.getRelativePath())  or
+                            srv.isProxySingleRunProgram(artifact.getRelativePath())
             </rule>
    
             <rule>persistent
@@ -335,29 +354,40 @@
                     -->
                <action>
                   fname = sprintf("%s%s%s.proxy.jast", 
-                                  io.getParent(file), 
+                                  io.getParent(artifact.getRelativePath()),
                                   fileSep, 
                                   proxyname)
                </action>
-               <action>proxyRoot = createJavaFile(fname)</action>
-               <action>proxyRoot.putAnnotation("filename", fname)</action>
+               <action>fartifact = art.createArtifact(fname)</action>
+               <action>proxyRoot = createJavaFile(fartifact)</action>
+               <action>proxyRoot.putAnnotation("filename", fartifact.getRelativePath())</action>
       
                <action>
-                  tw.graft("class_proxy_client_definition", null, proxyRoot, 
-                           "classname", proxyname,
-                           "pkgname", srv.getProxyPackage(file),
-                           "trackChanges", sprintf("%s", srv.isProxyTrackChanges(file)))
+                  tw.graft("class_proxy_client_definition",
+                           null,
+                           proxyRoot,
+                           "classname",
+                           proxyname,
+                           "pkgname",
+                           srv.getProxyPackage(artifact.getRelativePath()),
+                           "trackChanges",
+                           sprintf("%s", srv.isProxyTrackChanges(artifact.getRelativePath())))
                </action>
                <action>ref = createJavaAst(java.doc_comment, "", proxyRoot, 0)</action>
                <action>ref = createJavaAst(java.doc_paragraph, "", ref)</action>
       
                <!-- add package now -->
-               <action>createJavaAst(java.kw_package, srv.getProxyPackage(file), proxyRoot, 0)</action>
+               <action>
+                  createJavaAst(java.kw_package,
+                                srv.getProxyPackage(artifact.getRelativePath()),
+                                proxyRoot,
+                                0)
+               </action>
       
                <action>
                   createJavaAst(java.doc_textrun, 
-                                sprintf("Generated code for the legacy proxy object associated with file '%s'", 
-                                        file),
+                                sprintf("Generated code for the legacy proxy object associated with file '%s'",
+                                        artifact.getRelativePath()),
                                 ref)
                </action>
                
@@ -368,8 +398,12 @@
                <action>mthdAnchor = ref.getImmediateChild(java.cs_instance_methods, null)</action>
                <action>fldAnchor = ref.getImmediateChild(java.cs_constants, null)</action>
       
-               <action>srv.registerProxyJavaAst(file, fname)</action>
-               <action>srv.registerProxyClass(file, srv.getProxyPackage(file), proxyname)</action>
+               <action>srv.registerProxyJavaAst(artifact.getRelativePath(), fname)</action>
+               <action>
+                  srv.registerProxyClass(artifact.getRelativePath(),
+                                         srv.getProxyPackage(artifact.getRelativePath()),
+                                         proxyname)
+               </action>
             </rule>
          </rule>
       </init-rules>
@@ -386,15 +420,15 @@
                <rule>this.isRoot()
                   <action>type1 = proxyMethodName</action>
                   <action>type2 = "runProcedure"</action>
-                  <rule>srv.isProxyPersistentProgram(file)
+                  <rule>srv.isProxyPersistentProgram(artifact.getRelativePath())
                      <action>type1 = "runPersistent"</action>
                      <action>type2 = "runPersistentProcedure"</action>
                   </rule>
-                  <rule>srv.isProxySingletonProgram(file)
+                  <rule>srv.isProxySingletonProgram(artifact.getRelativePath())
                      <action>type1 = "runProcedure"</action>
                      <action>type2 = "runSingletonProcedure"</action>
                   </rule>
-                  <rule>srv.isProxySingleRunProgram(file)
+                  <rule>srv.isProxySingleRunProgram(artifact.getRelativePath())
                      <action>type1 = "runProcedure"</action>
                      <action>type2 = "runSingleRunProcedure"</action>
                   </rule>
@@ -421,7 +455,8 @@
          
                   <action>
                      createJavaAst(java.doc_textrun, 
-                                   sprintf("Generated code for executing legacy program from file %s", file),
+                                   sprintf("Generated code for executing legacy program from file %s",
+                                           artifact.getRelativePath()),
                                    ref)
                   </action>
                </rule>
@@ -450,7 +485,7 @@
                   <action>
                      createJavaAst(java.string, 
                                    sprintf("%s%s%s", 
-                                           srv.getProxyFolder(file), 
+                                           srv.getProxyFolder(artifact.getRelativePath()),
                                            fileSep, 
                                            getNoteString("basename")), 
                                    proxyref3)
@@ -480,7 +515,7 @@
                <action>argjtype = null</action>
                <action>argmode = null</action>
                
-               <action>unknownParams = srv.isProxyAllowUnknown(file)</action>
+               <action>unknownParams = srv.isProxyAllowUnknown(artifact.getRelativePath())</action>
                
                <rule>downPath(this, prog.kw_input)
                   <action>argmode = "input"</action>

=== modified file 'rules/convert/soap_services.xml'
--- old/rules/convert/soap_services.xml	2023-05-10 07:41:38 +0000
+++ new/rules/convert/soap_services.xml	2025-05-09 07:59:16 +0000
@@ -5,12 +5,14 @@
 ** Module   : soap_services.xml
 ** Abstract : Generates WSDL files for SOAP services.
 **
-** Copyright (c) 2021-2023, Golden Code Development Corporation.
+** Copyright (c) 2021-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 CA  20211009 Initial version.
 ** 002 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 003 DDF 20250423 Replace file with artifact.
+**     DDF 20250424 Replaced remaining file with artifact.
 */
 -->
 
@@ -84,7 +86,7 @@
 
    <rule-set input="tree">
       <init-rules>
-         <rule>schema.initializeDictionary(file)</rule>
+         <rule>schema.initializeDictionary(artifact)</rule>
          
          <rule>deleteDictionary("soap-operation")</rule>
          
@@ -100,7 +102,7 @@
          
          <rule>oname = lower(oname)</rule>
          <rule>addScope("soap-operation")</rule>
-         <rule>iter = srv.getSoapOperations(file, oname, null).iterator()</rule>
+         <rule>iter = srv.getSoapOperations(artifact.getRelativePath(), oname, null).iterator()</rule>
          <while>iter.hasNext()
             <action>soapOp = iter.next()</action>
             <action>addDictionaryBoolean("soap-operation", "getParm", true)</action>
@@ -148,7 +150,7 @@
                <action>iename = #(java.lang.String) ref.getAnnotation("name")</action>
             </rule>
             
-            <rule>iter = srv.getSoapOperations(file, oname, iename).iterator()</rule>
+            <rule>iter = srv.getSoapOperations(artifact.getRelativePath(), oname, iename).iterator()</rule>
             <while>iter.hasNext()
                <action>soapOp = iter.next()</action>
                <action>addDictionaryBoolean("soap-operation", "getParm", true)</action>
@@ -177,7 +179,7 @@
                <action>iename = #(java.lang.String) ref.getAnnotation("name")</action>
             </rule>
    
-            <action>srv.addSoapOperation(file, oname, iename)</action>
+            <action>srv.addSoapOperation(artifact.getRelativePath(), oname, iename)</action>
          </rule>
 
          <rule>evalLib("isTopLevelInternalNode", this) or this.type == prog.class_def
@@ -186,7 +188,7 @@
       </ascent-rules>
       
       <post-rules>
-         <rule>srv.addSoapOperation(file, oname, null)</rule>
+         <rule>srv.addSoapOperation(artifact.getRelativePath(), oname, null)</rule>
       </post-rules>
    </rule-set>
    

=== removed directory 'rules/dbref'
=== removed file 'rules/dbref/apply_refs.xml'
--- old/rules/dbref/apply_refs.xml	2020-04-13 11:17:30 +0000
+++ new/rules/dbref/apply_refs.xml	1970-01-01 00:00:00 +0000
@@ -1,230 +0,0 @@
-<?xml version="1.0"?>
-<!--
-/*
-** Module   : apply_refs.xml
-** Abstract : apply collected data to the schema definition
-**
-** Copyright (c) 2005-2020, Golden Code Development Corporation.
-**
-** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
-** 001 SIY 20050724   @21990 Initial implementation.
-** 002 CA  20200412          Added incremental conversion.  THIS FILE WAS NOT UPDATED TO USE IT!
-**                           CA: I think this is a dead file and should be removed.
-*/
--->
-<!--
-** 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.
--->
-
-<cfg>
-   <!-- register worker objects -->
-   <worker class="com.goldencode.p2j.uast.ProgressPatternWorker"
-           namespace="prog" />
-   <worker class="com.goldencode.p2j.convert.DatabaseReferenceWorker"
-           namespace="ref" />
-
-   <!-- variables -->
-   <variable name="tables" init="loadMap('dbref.xml')"/>
-
-   <!-- global functions -->
-   <func-library access="private">
-      <!-- dump collected table information in human readable form -->
-      <function name="dump_map">
-         <parameter name="map"    type="java.util.Map" />
-         <variable name="iter"    type="java.util.Iterator"/>
-         <variable name="iterFld" type="java.util.Iterator"/>
-         <variable name="fldMap"  type="java.util.Map"/>
-         <variable name="tmpSet"  type="java.util.TreeSet"/>
-         <variable name="var"     init="''"/>
-
-         <expression>
-            tmpSet = #(java.util.TreeSet) create('java.util.TreeSet', 
-                                                 map.keySet())
-         </expression>
-         <expression>
-            iter = tmpSet.iterator()
-         </expression>
-         <while>iter.hasNext()
-            <action>var = #(java.lang.String) iter.next()</action>
-            <action>fldMap = #(java.util.Map) map.get(var)</action>
-            <action>
-               printfln("%s, found %d fields:", var, fldMap.size())
-            </action>
-            <action>
-               tmpSet = #(java.util.TreeSet) create('java.util.TreeSet', 
-                                                    fldMap.keySet())
-            </action>
-            <action>iterFld = tmpSet.iterator()</action>
-            <while>iterFld != null and iterFld.hasNext()
-               <action>var = #(java.lang.String)iterFld.next()</action>
-               <action>printfln("   %s", var)</action>
-            </while>
-         </while>
-      </function>
-   </func-library>
-                
-   <!-- pipeline of rule-sets to process -->
-  
-   <!-- global initialization (once per pipeline) -->
-   <init-rules>
-      <!-- rule-set name="customer_specific_variables" optional="true"/ -->
-      <!-- rule>evalLib("dump_map", tables)</rule -->
-   </init-rules>
-  
-   <rule-set input="tree">
-      <include name="common-progress" />
-
-      <!-- local functions -->
-      <func-library access="private">
-         <!-- put a note -->
-         <function name="put_note">
-            <parameter name="name"  type="java.lang.String" />
-            <parameter name="value"  type="java.lang.Boolean" />
-            
-            <rule>name != null
-               <!-- action>
-                  printfln("%s referenced? %s", name, value)
-               </action -->
-               <action>putNote("referenced", value)</action>
-            </rule>
-         </function>
-      </func-library>
-
-      <!-- file-level variables -->
-      <init-rules>
-      </init-rules>
-
-      <!-- "in depth" tree traversal -->
-      <walk-rules>
-         <variable name="fields" type="java.util.Map" />
-         <variable name="tabname" type="java.lang.String" />
-      
-         <!-- add referenced fields -->
-         <rule>type == prog.TABLE
-            <action>
-               tabname = sprintf("%s.%s", 
-                                 parent.text.toLowerCase(), 
-                                 text.toLowerCase())
-            </action>
-            <rule>tables.get(tabname) != null
-               <action on="true">evalLib("put_note", tabname, true)</action>
-               <action on="false">evalLib("put_note", tabname, false)</action>
-            </rule>
-         </rule>
-         <rule>parent.type == prog.TABLE and 
-               (type == FIELD_CHAR    or
-                type == FIELD_DATE    or
-                type == FIELD_DEC     or
-                type == FIELD_INT     or
-                type == FIELD_LOGICAL or
-                type == FIELD_RECID   or
-                type == FIELD_ROWID   or
-                type == FIELD_HANDLE  or
-                type == FIELD_RAW)
-            <!-- action>
-               printfln("Field %s %s %s", 
-                        parent.parent.text, parent.text, text)
-            </action -->
-            <action>
-               tabname = sprintf("%s.%s", 
-                                 parent.parent.text.toLowerCase(), 
-                                 parent.text.toLowerCase())
-            </action>
-            <action>
-               fields = #(java.util.Map) tables.get(tabname)
-            </action>
-            <!-- action>
-               printfln("Fields %s ", fields == null)
-            </action -->
-            <!-- rule>fields != null
-               <action>
-                  printfln("Fields.get %s", fields.get(text.toLowerCase()))
-               </action>
-            </rule -->
-            <rule>fields == null or fields.get(text.toLowerCase()) == null
-               <action>tabname = sprintf("%s.%s", tabname, text)</action>
-               <action>evalLib("put_note", tabname, false)</action>
-               <!-- action>printfln("put_note %s %s", tabname, false)</action -->
-            </rule>
-            <rule>fields != null and fields.get(text.toLowerCase()) != null
-               <!-- field is referenced -->
-               <action>evalLib("put_note", tabname, true)</action>
-               
-               <!-- check if is is not listed in the excluded fields -->
-               <action>
-                  fields = #(java.util.Map) tables.get(sprintf("*%s", tabname))
-               </action>
-               <action>tabname = sprintf("%s.%s", tabname, text)</action>
-
-               <rule>fields != null and fields.get(text.toLowerCase()) != null
-                  <!-- excluded field -->
-                  <action>evalLib("put_note", tabname, false)</action>
-               </rule>
-            </rule>
-         </rule>
-      </walk-rules>
-
-      <!-- "step back" rules -->
-      <ascent-rules>
-      </ascent-rules>
-   </rule-set>
-
-   <post-rules>
-      <!-- rule>evalLib("dump_map", tables)</rule -->
-      <!-- rule>persistMap(tables, "dbref.xml")</rule -->
-      <rule-set>
-         <post-rules>
-            <rule>persist()</rule>
-         </post-rules>
-      </rule-set>
-   </post-rules>
-</cfg>
\ No newline at end of file

=== removed file 'rules/dbref/collect_refs.xml'
--- old/rules/dbref/collect_refs.xml	2020-04-13 11:17:30 +0000
+++ new/rules/dbref/collect_refs.xml	1970-01-01 00:00:00 +0000
@@ -1,527 +0,0 @@
-<?xml version="1.0"?>
-<!--
-/*
-** Module   : collect_refs.xml
-** Abstract : collect references to tables and fields
-**
-** Copyright (c) 2005-2020, Golden Code Development Corporation.
-**
-** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
-** 001 SIY 20050719   @21988 Initial implementation.
-** 002 CA  20200412          Added incremental conversion.  THIS FILE WAS NOT UPDATED TO USE IT!
-**                           CA: I think this is a dead file and should be removed.
-*/
--->
-<!--
-** 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.
--->
-
-<cfg>
-   <!-- register worker objects -->
-   <worker class="com.goldencode.p2j.uast.ProgressPatternWorker" 
-           namespace="prog" />
-   <worker class="com.goldencode.p2j.convert.DatabaseReferenceWorker"
-           namespace="ref" />
-
-   <!-- variables -->
-   <variable name="tables" 
-             init="#(java.util.Map) create('java.util.HashMap')"/>
-             
-   <func-library access="private">
-      <!-- dump collected table information in human readable form -->
-      <function name="dump_map">
-         <parameter name="map"    type="java.util.Map" />
-         <variable name="iter"    type="java.util.Iterator"/>
-         <variable name="iterFld" type="java.util.Iterator"/>
-         <variable name="fldMap"  type="java.util.Map"/>
-         <variable name="tmpSet"  type="java.util.TreeSet"/>
-         <variable name="var"     init="''"/>
-
-         <expression>
-            tmpSet = #(java.util.TreeSet) create('java.util.TreeSet', 
-                                                 map.keySet())
-         </expression>
-         <expression>
-            iter = tmpSet.iterator()
-         </expression>
-         <while>iter.hasNext()
-            <action>var = #(java.lang.String) iter.next()</action>
-            <action>fldMap = #(java.util.Map) map.get(var)</action>
-            <action>
-               printfln("%s, found %d fields:", var, fldMap.size())
-            </action>
-            <action>
-               tmpSet = #(java.util.TreeSet) create('java.util.TreeSet', 
-                                                    fldMap.keySet())
-            </action>
-            <action>iterFld = tmpSet.iterator()</action>
-            <while>iterFld != null and iterFld.hasNext()
-               <action>var = #(java.lang.String)iterFld.next()</action>
-               <action>printfln("   %s", var)</action>
-            </while>
-         </while>
-      </function>
-   </func-library>
-   
-   <!-- pipeline of rule-sets to process -->
-  
-   <!-- global initialization (once per pipeline) -->
-   <init-rules>
-      <!-- rule-set name="customer_specific_variables" optional="true"/ -->
-   </init-rules>
-  
-   <rule-set input="tree">
-      <include name="common-progress" />
-
-      <!-- file-level variables -->
-      <variable name="nestingLevel" init="#(long) 0" />
-      <variable name="canMatch"     init="false" />
-      <variable name="tmpTables" 
-                init="#(java.util.Map) create('java.util.HashMap')"/>
-                
-      <func-library access="private">
-         <!-- add table into list of referenced tables -->
-         <function name="add_table">
-            <parameter name="tbl" type="java.lang.String"/>
-            <variable  name="res" type="java.util.List" />
-            <variable  name="name" init="''" />
-   
-            <rule>tbl != null
-               <!-- split name -->
-               <action>
-                  res = createListFromArray(tbl.toLowerCase().split('\.'))
-               </action>
-               
-               <!-- check if table is in map -->
-               <rule>res != null      and 
-                     res.size() >= 2  and 
-                     tables.get(sprintf("%s.%s", res.get(0), res.get(1))) == null
-                  <action>
-                     tables.put(sprintf("%s.%s", res.get(0), res.get(1)), 
-                                create('java.util.HashMap'))
-                  </action>
-                  <!-- action>
-                     printfln("%s %d: INFO:: table %s.%s referenced",
-                              file, line, res.get(0), res.get(1))
-                  </action -->
-                  <action> name = #(java.lang.String) res.get(1)</action>                  
-                  <rule>res.get(0).equals("standard") or name.startsWith("_")
-                     <action>
-                        printfln("%s %d: INFO:: special table %s.%s referenced",
-                                 file, line, res.get(0), res.get(1))
-                     </action>
-                  </rule>
-               </rule>
-            </rule>
-         </function>
-         
-         <!-- add field into list of referenced fields -->
-         <function name="add_field">
-            <parameter name="fld"       type="java.lang.String" />
-            <variable  name="res"       type="java.util.List" />
-            <variable  name="fieldList" type="java.util.Map" />
-            
-            <rule>fld != null
-               <!-- split name -->
-               <action>
-                  res = createListFromArray(fld.toLowerCase().split('\.'))
-               </action>
-               
-               <rule>res != null and res.size() == 3
-                  <rule>tables.get(sprintf("%s.%s", res.get(0), res.get(1))) == null
-                     <action>
-                        printfln("%s %d: WARN:: field %s referenced before table",
-                                 file, line, fld)
-                     </action>
-                  </rule>
-                  <action>evalLib("add_table", fld)</action>
-                  <action>
-                     fieldList = tables.get(sprintf("%s.%s", res.get(0), res.get(1)))
-                  </action>
-                  <action>fieldList.put(res.get(2), fld)</action>
-               </rule>
-            </rule>
-         </function>
-
-         <!-- merge collected information into main list of tables/fields -->         
-         <function name="merge_fields">
-            <parameter name="tbls"  type="java.util.Map" />
-            <parameter name="flds"  type="java.util.Map" />
-            <variable  name="iter"  type="java.util.Iterator" />
-            <variable  name="fIter" type="java.util.Iterator" />
-            <variable  name="res"   type="java.util.List" />
-            <variable  name="var"   init="''" />
-            <variable  name="var2"  init="''" />
-            <variable  name="old"   type="java.util.Map" />
-            <variable  name="tmp"   type="java.util.Map" />
-            <variable  name="isect" type="java.util.Map" />
-            <variable  name="tabName"  init="''" />
-            
-            <!-- do not merge empty lists -->
-            <rule>tbls.size() > 0
-               <!-- action>
-                  printfln("%s %d: merging %d tables %d fields", 
-                           file, line, tbls.size(), flds.size())
-               </action>
-               <action>printfln("%s", getPath())</action -->
-               
-               <!-- load fields into tables -->
-               <action>iter = flds.keySet().iterator()</action>
-               <while>iter.hasNext()
-                  <action>var = #(java.lang.String) iter.next()</action>
-                  <action>
-                     res = createListFromArray(var.toLowerCase().split('\.'))
-                  </action>
-                  <rule>res.size() == 3
-                     <action>
-                        tabName = sprintf("%s.%s", res.get(0), res.get(1))
-                     </action>
-                     
-                     <rule>tbls.get(tabName) == null
-                        <action>
-                           tbls.put(tabName, create('java.util.HashMap'))
-                        </action>
-                     </rule>
-                     
-                     <action>
-                        tmp = #(java.util.Map) tbls.get(tabName)
-                     </action>
-
-                     <action>
-                        tmp.put(res.get(3), var)
-                     </action>
-                  </rule>
-               </while>
-               
-               <!-- 
-               Now table map contains all excluded fields. 
-               So, all we need to do is to merge (intersect) sets of fields.
-               -->
-               <action>iter = tbls.keySet().iterator()</action>
-               <while>iter.hasNext()
-                  <action>tabName = #(java.lang.String) iter.next()</action>
-                  <action>tmp = #(java.util.Map) tbls.get(tabName)</action>
-                  <action>
-                     res = createListFromArray(tabName.toLowerCase().split('\.'))
-                  </action>
-                  
-                  <rule>res.size() >= 2
-                     <action>
-                        isect = #(java.util.Map) create('java.util.HashMap')
-                     </action>
-
-                     <action>var = sprintf("*%s.%s", res.get(0), res.get(1))</action>
-                     <action>old = #(java.util.Map) tables.get(var)</action>
-                     <!-- intersect lists -->
-                     <rule>old != null
-                        <action>fIter = old.keySet().iterator()</action>
-                        <while>fIter.hasNext()
-                           <action>
-                              var2 = #(java.lang.String) iter.next()
-                           </action>
-                           <rule>old.get(var2) != null and 
-                                 tmp.get(var2) != null
-                              <action>
-                                 isect.put(var2, tmp.get(var2))
-                              </action>
-                           </rule>
-                        </while>
-                     </rule>
-
-                     <!-- table is not present in the map -->                     
-                     <rule>old == null
-                        <action>
-                           old = #(java.util.Map) create('java.util.HashMap')
-                        </action>
-                     </rule>
-                     
-                     <!-- replace existing list with new one -->
-                     <action>
-                        tables.put(var, isect)
-                     </action>
-                  </rule>
-               </while>
-            </rule>
-         </function>
-      </func-library>
-      
-      <init-rules>
-         <!-- clear per-file variables -->
-         <rule>tmpTables.clear()</rule>
-         <rule>nestingLevel = #(long) 0</rule>
-         <rule>canMatch = false</rule>
-      </init-rules>
-
-      <!-- "in depth" tree traversal -->
-      <walk-rules>
-         <variable name="exceptFields" type="java.util.Map" />
-         <variable name="fullTables"   type="java.util.Map" />
-         
-         <!-- add referenced fields -->
-         <rule>parent.type != KW_LIKE   and
-               parent.type != KW_EXCEPT and 
-               (type == FIELD_CHAR    or
-                type == FIELD_DATE    or
-                type == FIELD_DEC     or
-                type == FIELD_INT     or
-                type == FIELD_LOGICAL or
-                type == FIELD_RECID   or
-                type == FIELD_ROWID   or
-                type == FIELD_HANDLE  or
-                type == FIELD_RAW)
-            <!-- action>
-               printfln("%s %d: field %s referenced in %s", 
-                        file, line, getNoteString("schemaname"), parent)
-            </action -->
-            <action>
-               evalLib("add_field", getNoteString("schemaname"))
-            </action>
-         </rule>
-
-         <!-- add referenced tables -->
-         <rule>parent.type != KW_LIKE   and 
-               parent.type != KW_EXCEPT and 
-               type == prog.TABLE
-            <!-- action>
-               printfln("%s %d: table %s referenced in %s", 
-                        file, line, getNoteString("schemaname"), parent)
-            </action -->
-            
-            <action>
-               evalLib("add_table", getNoteString("schemaname"))
-            </action>
-         </rule>
-
-         <!-- process more complex cases -->
-         
-         <!-- following statements may refer all fields:
-            + ASSIGN record [EXCEPT field]
-            + BUFFER-COMPARE [EXCEPT field] *,** 
-            + BUFFER-COPY    [EXCEPT field] *,**
-            + DEFINE BROWSE ... DISPLAY ... 
-            + DEFINE FRAME ... [EXCEPT field]
-            + DEFINE QUERY ... EXCEPT *,***
-            + DISABLE ... ALL [EXCEPT field]
-            + DISPLAY ... [EXCEPT field]
-            + ENABLE ... ALL [EXCEPT field]
-            + EXPORT record [EXCEPT field]
-            + FORM record [EXCEPT field]
-            + IMPORT record [EXCEPT field]
-            + INSERT record [EXCEPT field]
-            + PROMPT-FOR record [EXCEPT field]
-            + SET record [EXCEPT field]
-            + UPDATE record [EXCEPT field]
-            + DO, FIND, FOR, OPEN QUERY, REPEAT, function CAN-FIND:
-            record-phrase EXCEPT [field]
-            
-            *     Note if USING phrase is present then statement does refer 
-                  only specified fields.
-            **    May contain NO-LOBS phrase which excludes LOBS from the list
-                  of referenced fields.
-            ***   Field list after EXCEPT can be empty, this means that all
-                  fields are referenced.
-            
-         -->
-
-         <!-- preliminary step -->
-         <!-- note that ENABLE/DISABLE is processed separately -->
-         <rule>type == KW_ASSIGN    or
-               type == KW_BUF_COMP  or
-               type == KW_BUF_COPY  or
-               type == KW_DEFINE    or
-               type == KW_DISP      or
-               type == KW_EXPORT    or
-               type == KW_FORM      or
-               type == KW_IMPORT    or
-               type == KW_INSERT    or
-               type == KW_PRMT_FOR  or
-               type == KW_SET       or
-               type == KW_UPDATE    
-            <action>
-               exceptFields = #(java.util.Map) create('java.util.HashMap')
-            </action>
-            <action>
-               fullTables = #(java.util.Map) create('java.util.HashMap')
-            </action>
-            <action>
-               canMatch = true
-            </action>
-         </rule>
-         
-         <!-- reset flag if this is wrong DEFINE statement -->
-         <rule>parent.type == KW_DEFINE and
-               type != KW_BROWSE        and
-               type != KW_FRAME         and
-               type != KW_QUERY
-            <action>canMatch = false</action>
-         </rule>
-         
-         <!-- presence of the USING phrase explicitly resets the flag -->
-         <rule>canMatch and type == KW_USING
-            <action>canMatch = false</action>
-         </rule>
-         
-         <!-- ENABLE/DISABLE ALL -->
-         <rule>type == KW_ALL and
-               (parent.type == KW_DISABLE or 
-                parent.type == KW_ENABLE)
-            <action>
-               exceptFields = #(java.util.Map) create('java.util.HashMap')
-            </action>
-            <action>
-               fullTables = #(java.util.Map) create('java.util.HashMap')
-            </action>
-            <action>
-               canMatch = true
-            </action>
-         </rule>
-         
-         <!-- Handle record_phrase. 
-              Note that only EXCEPT phrase actually matched all fields.
-         -->
-         <rule>canMatch != true             and
-               type == KW_EXCEPT            and
-               parent.type == RECORD_PHRASE
-            <action>
-               exceptFields = #(java.util.Map) create('java.util.HashMap')
-            </action>
-            <action>
-               fullTables = #(java.util.Map) create('java.util.HashMap')
-            </action>
-            <action>
-               canMatch = true
-            </action>
-         </rule>
-         
-         <!-- collect EXCEPT fields -->
-         <rule><!-- canMatch == true         and --> 
-               parent.type == KW_EXCEPT and
-               (type == FIELD_CHAR    or
-                type == FIELD_DATE    or
-                type == FIELD_DEC     or
-                type == FIELD_INT     or
-                type == FIELD_LOGICAL or
-                type == FIELD_RECID   or
-                type == FIELD_ROWID   or
-                type == FIELD_HANDLE  or
-                type == FIELD_RAW)
-            <!-- action>
-               printfln("%s %d: @@ collecting EXCEPT fields: %s ", 
-                        file, line, getNoteString("schemaname"))
-            </action -->
-            <action>
-               exceptFields.put(getNoteString("schemaname").toLowerCase(),
-                                getNoteString("schemaname"))
-            </action>
-         </rule>
-
-         <!-- report other usage of EXCEPT keyword (potential problems) -->
-         <rule>canMatch != true             and
-               type == KW_EXCEPT            and
-               parent.type != RECORD_PHRASE and
-               parent.type != KW_ALL        and
-               parent.getFirstChild().type != TEMP_TABLE
-               <action>
-                  printfln("%s %d: ERROR:: no handling for %s/%s/%s/%s", 
-                           file, 
-                           line, 
-                           parent.parent.parent,
-                           parent.parent,
-                           parent, 
-                           this)
-               </action>
-         </rule>
-
-         <!-- ignore TEMP_TABLE -->
-         <rule>canMatch and type == TEMP_TABLE
-            <action>canMatch = false</action>
-         </rule>
-
-         <!-- collect table names -->
-         <rule>canMatch and type == prog.TABLE
-            <action>
-               fullTables.put(getNoteString("schemaname").toLowerCase(),
-                              create('java.util.HashMap'))
-            </action>
-         </rule>
-      </walk-rules>
-
-      <!-- "step back" rules -->
-      <ascent-rules>
-      
-         <!-- final processing -->
-         <rule>canMatch and 
-               (type == KW_ASSIGN    or
-                type == KW_BUF_COMP  or
-                type == KW_BUF_COPY  or
-                type == KW_DEFINE    or
-                type == KW_DISP      or
-                type == KW_EXPORT    or
-                type == KW_FORM      or
-                type == KW_IMPORT    or
-                type == KW_INSERT    or
-                type == KW_PRMT_FOR  or
-                type == KW_SET       or
-                type == KW_UPDATE    or
-                type == RECORD_PHRASE)    
-            <action>canMatch = false</action>
-            <action>evalLib("merge_fields", fullTables, exceptFields)</action>
-         </rule>
-
-      </ascent-rules>
-   </rule-set>
-
-   <post-rules>
-      <!-- rule>evalLib("dump_map", tables)</rule -->
-      <rule>persistMap(tables, "dbref.xml")</rule>
-   </post-rules>
-</cfg>
\ No newline at end of file

=== modified file 'rules/fixups/collect_oo.rules'
--- old/rules/fixups/collect_oo.rules	2023-05-10 07:41:38 +0000
+++ new/rules/fixups/collect_oo.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : collect_oo.rules
 ** Abstract : gather qualified class and interface names across the project
 **
-** Copyright (c) 2018-2023, Golden Code Development Corporation.
+** Copyright (c) 2018-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 GES 20181205 Capture all fully qualified class and interface names across the project and
@@ -14,6 +14,7 @@
 ** 003 CA  20200428 Legacy DEFINE ENUM conversion support.
 ** 004 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 005 DDF 20250424 Replaced file with artifact.
 */
 -->
 
@@ -104,7 +105,7 @@
 
          <!-- this is safe because one can only have a single class OR interface def per file -->
          <!-- 4GL has no inner classes -->
-         <action>fnames.add(file)</action>
+         <action>fnames.add(artifact.getRelativePath())</action>
          
       </rule>
          

=== modified file 'rules/fixups/collect_procedures.rules'
--- old/rules/fixups/collect_procedures.rules	2023-05-10 07:41:38 +0000
+++ new/rules/fixups/collect_procedures.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : collect_procedures.rules
 ** Abstract : gather procedure definitions across the project
 **
-** Copyright (c) 2018-2023, Golden Code Development Corporation.
+** Copyright (c) 2018-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 GES 20180520 Capture all internal procedure definitions across the project and store them
@@ -13,6 +13,7 @@
 ** 002 CA  20200412 Added incremental conversion support.
 ** 003 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 004 DDF 20250424 Replaced file with artifact.
 */
 -->
 
@@ -93,7 +94,7 @@
       </rule>
    
       <!-- calc the base 4GL name (no pathing) -->
-      <rule>basename = execLib("simple_filename", file)</rule>
+      <rule>basename = execLib("simple_filename", artifact.getRelativePath())</rule>
       <rule>putNote("basename", basename)</rule>
       
       <!-- remove case for insensitive legacy systems -->
@@ -105,7 +106,7 @@
          <action>
             printfln("WARNING: multiple external procedures share basename %s (filename %s).\n",
                      basename,
-                     file)
+                     artifact.getRelativePath())
          </action>
          <action on="false">extProcBasenames.add(basename)</action>
       </rule>

=== modified file 'rules/fixups/functions_procedures.rules'
--- old/rules/fixups/functions_procedures.rules	2023-05-10 06:23:45 +0000
+++ new/rules/fixups/functions_procedures.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : functions_procedures.rules
 ** Abstract : handle AST fixes for function and procedure definitions.
 **
-** Copyright (c) 2013-2023 Golden Code Development Corporation.
+** Copyright (c) 2013-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 CA  20130118 Added support for persistent procedures,  procedure 
@@ -55,6 +55,7 @@
 **                  more gracefully. Ref. #7190.
 ** 015 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 016 DDF 20250424 Replaced file with artifact.
 */
 -->
 
@@ -212,7 +213,7 @@
             <rule>evalLib("read_only_attribute", ref)
                <action>
                   printfln("READ_ONLY_WARNING: REWRITING attribute assignment as string at %s line %05d column %05d",
-                           file,
+                           artifact.getRelativePath(),
                            line,
                            this.getColumn())
                </action>

=== modified file 'rules/fixups/post_parse_fixups.xml'
--- old/rules/fixups/post_parse_fixups.xml	2024-12-04 14:45:40 +0000
+++ new/rules/fixups/post_parse_fixups.xml	2025-05-09 07:59:16 +0000
@@ -4,7 +4,7 @@
 ** Module   : post_parse_fixups.xml
 ** Abstract : handle essential tree creation/cleanup that can't be done while parsing
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ _________________________________ Description _________________________________
 ** 001 GES 20050726   @21837 This version modifies AST nodes after the
@@ -124,6 +124,7 @@
 **                           versions, based on node types rather on string paths.
 ** 052 CA  20230315          Hide empty UI statements (without any widgets, like 'UPDATE.').
 ** 053 EM  20241113          DISPLAY won't morph into a VIEW.
+** 054 DDF 20250424          Replaced file with artifact.
 */
 -->
 
@@ -333,7 +334,7 @@
             <action>putNote("vardef", true)</action>
             <action>fprintf('post_parse_fixups.log',
                             'VAR_DEF  %-40s: %-25s @ %05d : %05d\n',
-                            file,
+                            artifact.getRelativePath(),
                             text, 
                             line,
                             this.getColumn())
@@ -365,7 +366,7 @@
             <action>putNote("widdef", true)</action>
             <action>fprintf("post_parse_fixups.log",
                             "WID_DEF  %-40s: %-25s @ %05d : %05d\n",
-                            file,
+                            artifact.getRelativePath(),
                             this.getSymbolicTokenType(), 
                             line,
                             this.getColumn())
@@ -412,7 +413,7 @@
             <action>removeNote("tempidx-file")</action>
             <action>fprintf("post_parse_fixups.log",
                             "VAR_REF  %-40s: %-25s @ %05d : %05d\n",
-                            file,
+                            artifact.getRelativePath(),
                             this.getSymbolicTokenType(), 
                             line,
                             this.getColumn())
@@ -471,7 +472,7 @@
          <rule>dictionaryContains("internal_procs", text) 
             <action>fprintf("post_parse_fixups.log",
                             "INT_PROC %-40s: %-25s @ %05d : %05d\n",
-                            file,
+                            artifact.getRelativePath(),
                             text, 
                             line,
                             this.getColumn())

=== modified file 'rules/fixups/variable_initializers.rules'
--- old/rules/fixups/variable_initializers.rules	2017-08-27 15:53:39 +0000
+++ new/rules/fixups/variable_initializers.rules	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : variable_initializers.rules
 ** Abstract : identify invalid init constructs and fix if possible
 **
-** Copyright (c) 2006-2017, Golden Code Development Corporation.
+** Copyright (c) 2006-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
 ** 001 GES 20060427   @25766 Identify invalid init constructs and fix if possible.
@@ -22,6 +22,7 @@
 ** 009 CA  20130313          Removed the char initializer fixup, as this is parser's job.
 ** 010 CA  20170827          Function parameters can be defined "LIKE table.field" starting with 
 **                           OE version 11.
+** 011 DDF 20250424          Replaced file with artifact.
 */
  -->
 <!--
@@ -103,7 +104,7 @@
          <action>copy.remove()</action>
          <action>fprintf("post_parse_fixups.log",
                          "DEADINIT %-40s: %-25s @ %05d : %05d\n",
-                         file,
+                         artifact.getRelativePath(),
                          text, 
                          line,
                          this.getColumn())
@@ -126,7 +127,7 @@
             <rule>extent &gt; 0 and numImmediateChildren &gt; extent
                <action>fprintf("post_parse_fixups.log",
                                "BADINIT  %-40s: %-25s @ %05d : %05d\n",
-                               file,
+                               artifact.getRelativePath(),
                                text, 
                                line,
                                this.getColumn())

=== modified file 'rules/include/common-progress.rules'
--- old/rules/include/common-progress.rules	2025-04-23 16:27:39 +0000
+++ new/rules/include/common-progress.rules	2025-05-09 07:59:16 +0000
@@ -606,6 +606,7 @@
    <worker class="com.goldencode.p2j.convert.NameMappingWorker"          namespace="nmap"/>
    <worker class="com.goldencode.p2j.xml.XmlPatternWorker"               namespace="xml" />
    <worker class="com.goldencode.p2j.schema.P2OAccessWorker"             namespace="p2o" />
+   <worker class="com.goldencode.artifacts.ArtifactWorker"               namespace="art" />
 
    <include name="i18n" />
 
@@ -2044,7 +2045,8 @@
               P2J home directory (the registry does this), this strips
               the file portion off and leaves the preceding path -->
          <rule>
-            pkg = file.substring(0, (file.lastIndexOf(fileSep) + num))
+            pkg = artifact.getRelativePath()
+                          .substring(0, (artifact.getRelativePath().lastIndexOf(fileSep) + num))
          </rule>
 
          <rule>pkg.equals(basepath)
@@ -12000,5 +12002,22 @@
             <rule on="false">putNote("javaname", javaName)</rule>
          </rule>
       </function>
+
+      <!-- generate menu class artifact (associated file does not have an extension) -->
+      <function name="gen_fartifact">
+         <parameter name="cname"    type="java.lang.String" />
+         <variable name="fname"     type="java.lang.String" />
+         <return name="fartifact"   type="com.goldencode.artifacts.Artifact" />
+
+         <rule>true
+            <action>fname = artifact.getRelativePath()</action>
+            <action>
+               fname = fname.substring(0, artifact.getRelativePath().lastIndexOf(fileSep))
+            </action>
+
+            <action>fname = sprintf("%s%s%s", fname, fileSep, cname)</action>
+            <action>fartifact = art.createArtifact(fname)</action>
+         </rule>
+      </function>
   </func-library>
 </rule-set>

=== modified file 'rules/progress/anti_parser.xml'
--- old/rules/progress/anti_parser.xml	2018-10-24 18:55:46 +0000
+++ new/rules/progress/anti_parser.xml	2025-05-09 07:59:16 +0000
@@ -5,10 +5,11 @@
 ** Module   : anti_parser.xml
 ** Abstract : anti-parses ASTs into Progress 4GL source code 
 **
-** Copyright (c) 2018, Golden Code Development Corporation.
+** Copyright (c) 2018-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ ________________________________Description_________________________________
 ** 001 GES 20181023 First version.
+** 002 DDF 20250424 Replaced file with artifact.
 */
  -->
  
@@ -141,7 +142,7 @@
    <rule-set input="tree" >
       <init-rules>
          <!-- build our full name -->
-         <rule>outname = sprintf("%s.mod", file)</rule>
+         <rule>outname = sprintf("%s.mod", artifact.getRelativePath())</rule>
          
          <!-- delete the file if it exists -->
          <rule>deleteFile(outname, false)</rule>

=== modified file 'rules/reports/consolidated_reports.xml'
--- old/rules/reports/consolidated_reports.xml	2023-05-10 07:41:38 +0000
+++ new/rules/reports/consolidated_reports.xml	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : consolidated_reports.xml
 ** Abstract : all reports run in a single consolidated walk
 **
-** Copyright (c) 2013-2023, Golden Code Development Corporation.
+** Copyright (c) 2013-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 GES 20130201 First version.
@@ -15,6 +15,8 @@
 ** 005 CA  20200412 Added incremental conversion support.
 ** 006 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 007 DDF 20250423 Replaced filename with artifact.
+**     DDF 20250424 Replaced file with artifact.
 */
 -->
 
@@ -132,7 +134,7 @@
          <variable  name="idx"   type="java.lang.Integer" />
          <return    name="cpath" type="java.lang.String" />
          
-         <rule>cpath = this.filename</rule>
+         <rule>cpath = this.artifact.getRelativePath()</rule>
          <rule>idx = cpath.lastIndexOf(fileSep)</rule>
          <rule>idx == -1
             <action>throwException(sprintf("Invalid path %s.", cpath))</action>
@@ -177,7 +179,7 @@
          
          <rule>hints != null and this.line != 0 and this.column != 0
             <action>nextIncl = hints.getSourceFor(this.line, this.column)</action>
-            <rule>!nextIncl.equals(file) and (lastIncl == null or !nextIncl.equals(lastIncl))
+            <rule>!nextIncl.equals(artifact.getRelativePath()) and (lastIncl == null or !nextIncl.equals(lastIncl))
                <action>lastIncl = nextIncl</action>
                <action>isIncl = true</action>
             </rule>
@@ -422,7 +424,7 @@
             
             <!-- add a row of data for this file in the per file report -->
             <rule>current = create("java.util.LinkedList")</rule>
-            <rule>current.add(file)</rule>
+            <rule>current.add(artifact.getRelativePath())</rule>
             <rule>current.add(num)</rule>
             <rule>dnum = num - inum</rule>
             <rule>current.add(dnum)</rule>

=== modified file 'rules/reports/file_filter.xml'
--- old/rules/reports/file_filter.xml	2017-04-01 23:33:34 +0000
+++ new/rules/reports/file_filter.xml	2025-05-09 07:59:16 +0000
@@ -5,10 +5,11 @@
 ** Module   : file_filter.xml
 ** Abstract : filter sources files that meet a criteria
 **
-** Copyright (c) 2016-2017, Golden Code Development Corporation.
+** Copyright (c) 2016-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description_________________________________
-** 001 OM  20160426 Initial version. Outputs only source files names that met specified criteria. 
+** 001 OM  20160426 Initial version. Outputs only source files names that met specified criteria.
+** 002 DDF 20250424 Replaced file with artifact.
 */
  -->
 <!--
@@ -86,7 +87,7 @@
       
       <walk-rules>
          <rule>!found and (#(java.lang.Boolean) exec(criteria))
-            <action>printfln("%s", file)</action>
+            <action>printfln("%s", artifact.getRelativePath())</action>
             <action>found = true</action>
          </rule>
       </walk-rules>

=== modified file 'rules/reports/list_classes.xml'
--- old/rules/reports/list_classes.xml	2023-05-10 06:23:45 +0000
+++ new/rules/reports/list_classes.xml	2025-05-09 07:59:16 +0000
@@ -5,12 +5,13 @@
 ** Module   : list_classses.xml
 ** Abstract : List all classes and their definition files.
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 CA  20170713 Created initial version.
 ** 002 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 003 DDF 20250423 Replaced filename with artifact.
 */
 -->
 
@@ -94,7 +95,7 @@
                <action on="false">cnames.put(cname, fnames)</action>
             </rule>
 
-            <action>fnames.add(this.filename)</action>
+            <action>fnames.add(this.artifact.getRelativePath())</action>
          </rule>
       </walk-rules>
    </rule-set>

=== modified file 'rules/reports/pphints_check.xml'
--- old/rules/reports/pphints_check.xml	2017-08-30 08:22:46 +0000
+++ new/rules/reports/pphints_check.xml	2025-05-09 07:59:16 +0000
@@ -5,10 +5,11 @@
 ** Module   : pphints_check.xml
 ** Abstract : check which pphints files fail to load, as are not generated correctly.
 **
-** Copyright (c) 2017, Golden Code Development Corporation.
+** Copyright (c) 2017-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description_________________________________
-** 001 CA  20170829 Initial version. Outputs only source files names that can't load the pphints. 
+** 001 CA  20170829 Initial version. Outputs only source files names that can't load the pphints.
+** 002 DDF 20250423 Replaced filename with artifact.
 */
  -->
 <!--
@@ -78,7 +79,7 @@
    <rule-set input="tree" >
       <init-rules>
          <rule>!preproc.loadHints()
-            <action>fprintf(fname, "%s.pphints\n", this.filename)</action>
+            <action>fprintf(fname, "%s.pphints\n", this.artifact.getRelativePath())</action>
          </rule>
       </init-rules>
    </rule-set>

=== modified file 'rules/reports/rpt_file_list.xml'
--- old/rules/reports/rpt_file_list.xml	2019-10-23 17:50:00 +0000
+++ new/rules/reports/rpt_file_list.xml	2025-05-09 07:59:16 +0000
@@ -6,12 +6,13 @@
 ** Abstract : provides a simple command-line mechanism to make a list of files
 **            matching a user-specified condition
 **
-** Copyright (c) 2013-2019, Golden Code Development Corporation.
+** Copyright (c) 2013-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ ________________________________Description_________________________________
 ** 001 GES 20130109 First version.
 ** 002 VVT 20190723 The databaseMode option is not needed anymore and was removed.
 ** 003 VVT 20190726 Matching the condition should be allowed at any node.
+** 004 DDF 20250424 Replaced file with artifact.
 */
  -->
  
@@ -103,7 +104,7 @@
       <init-rules>
          <rule>isNote("srcfile")
             <action>srcfile = getNoteString("srcfile")</action>
-            <action on='false'>srcfile = file</action>
+            <action on='false'>srcfile = artifact.getRelativePath()</action>
          </rule>
       </init-rules>
       <walk-rules>

=== modified file 'rules/reports/rpt_run_stmt_file_invocation.xml'
--- old/rules/reports/rpt_run_stmt_file_invocation.xml	2017-06-04 22:05:47 +0000
+++ new/rules/reports/rpt_run_stmt_file_invocation.xml	2025-05-09 07:59:16 +0000
@@ -6,7 +6,7 @@
 ** Abstract : generates a report of all run stmts that invoke a specified
 **            set of files
 **
-** Copyright (c) 2005-2017, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
 ** 001 GES 20051128   @23550 Generates a report of all run stmts that
@@ -14,7 +14,8 @@
 ** 002 GES 20090422   @41887 Converted format specs into standard J2SE form.
 ** 003 GES 20130206          Moved StatsWorker to pattern directory and changed
 **                           the name.
-** 002 ECF 20170604          Relocated ReportWorker to p2j.report package.
+** 004 ECF 20170604          Relocated ReportWorker to p2j.report package.
+** 005 DDF 20250424          Replaced file with artifact.
 */
  -->
 <!--
@@ -185,7 +186,7 @@
       <!-- per file init -->
       <init-rules>
          <rule>idx = 0</rule>
-         <rule>selfRef = evalLib("detect_match", file)</rule>
+         <rule>selfRef = evalLib("detect_match", artifact.getRelativePath())</rule>
       </init-rules>
       
       <!-- per file walk -->

=== modified file 'rules/reports/simple_search.xml'
--- old/rules/reports/simple_search.xml	2023-05-10 07:41:38 +0000
+++ new/rules/reports/simple_search.xml	2025-05-09 07:59:16 +0000
@@ -5,7 +5,7 @@
 ** Module   : simple_search.xml
 ** Abstract : provides a simple command-line tree searching tool
 **
-** Copyright (c) 2007-2017, Golden Code Development Corporation.
+** Copyright (c) 2007-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
 ** 001 GES 20070511   @33476 Good working version which provides a simple
@@ -14,6 +14,7 @@
 ** 002 GES 20130117          Added the reports include since this has many
 **                           useful matching methods.
 ** 003 ECF 20170606          Retrofitted for use with report server.
+** 004 DDF 20250423          Replaced filename with artifact.
 */
  -->
 <!--
@@ -107,7 +108,7 @@
                   <action>nextRow = create("com.goldencode.p2j.report.server.DetailRow")</action>
                   <action>nextId = nextId + 1</action>
                   <action>nextRow.id = nextId</action>
-                  <action>nextRow.file = this.filename</action>
+                  <action>nextRow.file = this.artifact.getRelativePath()</action>
                   <action>nextRow.astid = this.id</action>
                   <action>nextRow.line = this.line</action>
                   <action>nextRow.col = this.column</action>

=== modified file 'rules/schema/annotations.xml'
--- old/rules/schema/annotations.xml	2020-04-10 16:43:08 +0000
+++ new/rules/schema/annotations.xml	2025-05-09 07:59:16 +0000
@@ -4,7 +4,7 @@
 ** Abstract : walks Progress source code to extract schema-relevant
 **            information
 **
-** Copyright (c) 2005-2017, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
 ** 001 ECF 20051013   @23082 Good working version. Walks Progress source
@@ -19,6 +19,8 @@
 ** 005 OM  20130808          Added annotations with the fields used in a OF table join.
 **                           Added persistence of copy AST.
 ** 006 ECF 20150325          Added support for cross-schema natural joins.
+** 007 DDF 20250423          Replaced file with artifact.
+**     DDF 20250424          Replaced remaining file with artifact.
 -->
 <!--
 ** This program is free software: you can redistribute it and/or modify
@@ -108,7 +110,7 @@
       
       <init-rules>
          <!-- load schema data associated with this AST -->
-         <rule>schema.initializeDictionary(file)</rule>
+         <rule>schema.initializeDictionary(artifact)</rule>
       </init-rules>
       
       <walk-rules>
@@ -146,7 +148,7 @@
                <action>right = this.getAnnotation("schemaname")</action>
                <action>join = schema.recordJoin(left, right)</action>
                <rule>join != null and join.databases.contains("@temp_db")
-                  <action>tempJoinFiles.add(file)</action>
+                  <action>tempJoinFiles.add(artifact.getRelativePath())</action>
                </rule>
                <action>foreignKey = schema.getForeignKey(left, right)</action>
                

=== modified file 'rules/schema/dmo_common.rules'
--- old/rules/schema/dmo_common.rules	2025-02-18 11:48:12 +0000
+++ new/rules/schema/dmo_common.rules	2025-05-09 07:59:16 +0000
@@ -159,6 +159,7 @@
 ** 066 OM  20240910          Added tenantType attribute for @Table annotations.
 ** 067 CA  20241126          Fixed 'buildDbImportProperties' with 'jdbcBatchSize' argument.
 ** 068 DDF 20240205          Add an additional field to index components for the not-null attribute.
+** 069 DDF 20250423          Replaced filename usages with artifacts.
 -->
 
 <!--
@@ -612,7 +613,7 @@
       
       <!-- create the compile unit; the skeleton of the JAST -->
       <function name="createCompileUnit">
-         <parameter name="filename"    type="java.lang.String" />
+         <parameter name="fileArtifact" type="com.goldencode.artifacts.Artifact" />
          <parameter name="package"     type="java.lang.String" />
          <parameter name="className"   type="java.lang.String" />
          <parameter name="permanent"   type="java.lang.Boolean" />
@@ -637,12 +638,14 @@
          <variable  name="varRoot"           type="com.goldencode.ast.Aast" />
          <variable  name="methRoot"          type="com.goldencode.ast.Aast" />
          <variable  name="legacyTable"       type="java.lang.String" />
-         
+
          <return    name="classAst"          type="com.goldencode.ast.Aast" />
          
          <!-- create java file; generates AST root for java class -->
-         <rule>!java.createJavaFile(filename, true, isRuntimeConfig())
-            <action>throwException(sprintf('Error creating java AST:  %s', filename))</action>
+         <rule>!java.createJavaFile(fileArtifact, true, isRuntimeConfig())
+            <action>
+               throwException(sprintf('Error creating java AST:  %s', fileArtifact.getRelativePath()))
+            </action>
          </rule>
          
          <!-- add package statement -->
@@ -1341,19 +1344,18 @@
       
       <!-- persist the JAST currently built up inside the Java pattern worker -->
       <function name="persistJast">
-      
-         <parameter name="filename" type="java.lang.String" />
-         
+         <parameter name="fileArtifact" type="com.goldencode.artifacts.Artifact" />
+
          <!-- persist Java AST if class was not dropped -->
          <rule>!this.isAnnotation('drop')
-            <action on="true">printfln('%s.jast', filename)</action>
-            <rule on="true">!java.persistJavaFile(filename)
+            <action on="true">printfln('%s.jast', fileArtifact.getRelativePath())</action>
+            <rule on="true">!java.persistJavaFile(fileArtifact)
                <action>
-                  printfln('Warning:  did not persist empty java AST:  %s', filename))
+                  printfln('Warning:  did not persist empty java AST:  %s', fileArtifact.getRelativePath()))
                </action>
             </rule>
             
-            <action on="false">printfln('DMO dropped:  %s', filename)</action>
+            <action on="false">printfln('DMO dropped:  %s', fileArtifact.getRelativePath())</action>
          </rule>
       </function>
       

=== modified file 'rules/schema/fixups.xml'
--- old/rules/schema/fixups.xml	2024-04-29 14:52:25 +0000
+++ new/rules/schema/fixups.xml	2025-05-09 07:59:16 +0000
@@ -3,7 +3,7 @@
 ** Module   : fixups.xml
 ** Abstract : walks Progress database schema AST to simplify structure
 **
-** Copyright (c) 2004-2024, Golden Code Development Corporation.
+** Copyright (c) 2004-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 ECF 20050815   @22145 Good working version. Walks Progress schema
@@ -103,6 +103,8 @@
 **                           the before-table fields.  Also, a field with 'LIKE book.book-id' where book-id
 **                           is a MANDATORY field in a database schema, will not inherit the MANDATORY
 **                           option.
+** 047 DDF 20250423          Replaced filename usages with artifacts.
+**     DDF 20250424          Replaced file with artifact.
 */
 -->
 
@@ -212,6 +214,7 @@
    <worker class="com.goldencode.p2j.uast.UastHintsWorker"               namespace="hints" />
    <worker class="com.goldencode.p2j.convert.ExpressionConversionWorker" namespace="ecw" />
    <worker class="com.goldencode.p2j.schema.SchemaWorker"                namespace="schema" />
+   <worker class="com.goldencode.artifacts.ArtifactWorker"               namespace="art" />
    
    <!-- function libraries -->
    <include name="common-progress" />
@@ -278,7 +281,6 @@
       <variable name="idx"            type="java.lang.Integer" />
       <variable name="tableName"      type="java.lang.String" />
       <variable name="metaName"       type="java.lang.String" />
-      <variable name="metaDictFile"   type="java.lang.String" />
       <variable name="metaSrcFile"    type="java.lang.String" />
       <variable name="dumpFile"       type="java.lang.String" />
       <variable name="permMetaTables" type="java.util.Set" />
@@ -287,6 +289,7 @@
       <variable name="iter"           type="java.util.Iterator" />
       <variable name="tableAst"       type="com.goldencode.ast.Aast" />
       <variable name="metaSchema"     type="com.goldencode.ast.Aast" />
+      <variable name="metaDictArtifact" type="com.goldencode.artifacts.Artifact" />
       
       <init-rules>
          <!-- store the name of the metadata schema for this project -->
@@ -336,15 +339,15 @@
                      </rule>
                      
                      <!-- lazily load the metaschema AST -->
-                     <rule>metaDictFile == null
+                     <rule>metaDictArtifact == null
                         <action>
-                           metaDictFile = schema.schemaConfig.getSchemaFile(metaName, ".dict").getPath()
+                           metaDictArtifact = schema.schemaConfig.getSchemaFile(metaName, ".dict")
                         </action>
-                        <action>metaSchema = loadTree(metaDictFile)</action>
+                        <action>metaSchema = loadTree(metaDictArtifact)</action>
                         <rule>metaSchema == null
                            <action>
                               throwException("ERROR:  metaschema could not be loaded from %s",
-                                             metaDictFile.toString())
+                                             metaDictArtifact.getRelativePath())
                            </action>
                         </rule>
                         <rule>metaSchema.isAnnotation("srcfile")
@@ -366,7 +369,8 @@
                      <rule>tableAst == null
                         <action>
                            throwException("ERROR:  metadata table '%s' could not be found in %s",
-                                          tableName, metaDictFile.toString())
+                                          tableName,
+                                          metaDictArtifact.getRelativePath())
                         </action>
                      </rule>
                      
@@ -484,16 +488,23 @@
       <variable name="fldTrgEvent" type="java.lang.String" />
       <variable name="tmpAst" type="com.goldencode.ast.Aast" />
       <variable name="refAst" type="com.goldencode.ast.Aast" />
+      <variable name="fixupArtifact"    type="com.goldencode.artifacts.Artifact" />
       <variable name="tmp" type="java.lang.String" />
       
       <init-rules>
          <!-- replace original filename extension with '.schema' -->
          <rule>
-            fixupFile = sprintf('%s.schema', file.substring(0, file.lastIndexOf('.')))
+            fixupFile = sprintf('%s.schema',
+                                artifact.getRelativePath()
+                                        .substring(0, artifact.getRelativePath().lastIndexOf('.')))
+         </rule>
+
+         <rule>
+            fixupArtifact = art.createArtifact(fixupFile)
          </rule>
          
          <!-- reinitialize copy AST and register it under its new name -->
-         <rule>copy.brainwash(fixupFile)</rule>
+         <rule>copy.brainwash(fixupArtifact)</rule>
          
          <!-- initialize the indexComponents -->
          <rule>indexComponents = create("java.util.HashSet")</rule>
@@ -981,7 +992,7 @@
       <post-rules>
       
          <!-- save the fixed-up schema AST -->
-         <rule>persist(copy.filename, true, false)</rule>
+         <rule>persist(copy.artifact, true, false)</rule>
          
       </post-rules>
       

=== modified file 'rules/schema/iface_infra.rules'
--- old/rules/schema/iface_infra.rules	2020-06-10 17:30:34 +0000
+++ new/rules/schema/iface_infra.rules	2025-05-09 07:59:16 +0000
@@ -3,7 +3,7 @@
 ** Module   : iface_infra.rules
 ** Abstract : rules to generate Java DMO interface AST infrastructure
 **
-** Copyright (c) 2005-2020, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
 ** 001 ECF 20050922   @22940 Created initial version. Provides creation rules
@@ -21,6 +21,7 @@
 ** 007 ECF 20160222          Added bufIfaceAst as an attach point for legacy methods.
 ** 008 OM  20181214          Added support for class fields.
 ** 009 OM  20200108          Moved back the extent support method definition to main interface.
+** 010 DDF 20250423          Replaced javaFile with javaArtifact in createCompileUnit call.
 -->
 
 <!--
@@ -129,11 +130,12 @@
                                fileSep,
                                ifaceName)
          </action>
+         <action>javaArtifact = art.createArtifact(javaFile)</action>
          
          <!-- create compile unit, the skeleton of the JAST -->
          <action>
             classAst = execLib('createCompileUnit',
-                               javaFile,
+                               javaArtifact,
                                package,
                                ifaceName,
                                #(boolean) parent.getAnnotation('permanent'),

=== modified file 'rules/schema/java_dmo.xml'
--- old/rules/schema/java_dmo.xml	2025-03-25 23:14:51 +0000
+++ new/rules/schema/java_dmo.xml	2025-05-09 07:59:16 +0000
@@ -42,6 +42,7 @@
 **     CA  20220413          Configured format for 'object' fields.
 ** 017 OM  20250110          Added sequence support for multi-tenant databases.
 ** 018 OM  20250225          Unified multi-tenant annotations for tables and sequences.
+** 019 DDF 20250423          Use artifacts instead of filenames in function calls.
 -->
 
 <!--
@@ -109,7 +110,8 @@
    <worker class="com.goldencode.p2j.uast.JavaPatternWorker" namespace="java" />
    <worker class="com.goldencode.p2j.pattern.TemplateWorker" namespace="tpl" />
    <worker class="com.goldencode.p2j.schema.SchemaWorker"    namespace="schema" />
-   
+   <worker class="com.goldencode.artifacts.ArtifactWorker"   namespace="art" />
+
    <!-- function library includes -->
    <include name="schema/dmo_common" />
    
@@ -121,6 +123,7 @@
    <variable name="view"               type="java.lang.Boolean" />
    <variable name="classAst"           type="com.goldencode.ast.Aast" />
    <variable name="ctorAst"            type="com.goldencode.ast.Aast" />
+   <variable name="javaArtifact"       type="com.goldencode.artifacts.Artifact" />
    <variable name="defFormats"         type="java.util.Map" />
    
    <!-- pipeline of rule-sets to process -->
@@ -214,6 +217,7 @@
       <variable name="ddlSchema"    type="java.lang.String" />
       <variable name="dbName"       type="java.lang.String" />
       <variable name="seqFile"      type="java.lang.String" />
+      <variable name="seqArtifact"  type="com.goldencode.artifacts.Artifact" />
       <variable name="sequencesAst" type="com.goldencode.ast.Aast" />
       <variable name="seqEnumAst"   type="com.goldencode.ast.Aast" />
       
@@ -233,14 +237,16 @@
          <rule>type == data.class
             <rule>isRuntimeConfig()
                <action>storeObject("iface", text, getAst(javaRootId))</action>
-               <action on="false">execLib('persistJast', javaFile)</action>
+               <action on="false">execLib('persistJast', javaArtifact)</action>
             </rule>
          </rule>
          
          <rule>type == data.data_model and sequencesAst != null and !isRuntimeConfig()
             <!-- create java file; generates AST roots for java enum class -->
-            <rule>!java.createJavaFile(seqFile, true, false)
-               <action>throwException(sprintf("Error creating java AST: %s", seqFile))</action>
+            <rule>!java.createJavaFile(seqArtifact, true, false)
+               <action>
+                  throwException(sprintf("Error creating java AST: %s", seqArtifact.getRelativePath()))
+               </action>
             </rule>
             
             <!-- add package statement -->
@@ -261,9 +267,12 @@
             <!-- now we can graft the enum class that wes previously built -->
             <action>getAst(javaRootId).graft(sequencesAst)</action>
             
-            <action>printfln("%s.jast", seqFile)</action>
-            <rule>!java.persistJavaFile(sprintf('%s.jast', seqFile))
-               <action>printfln("Failed to save JAST for %s", seqFile)</action>
+            <action>printfln("%s.jast", seqArtifact.getRelativePath())</action>
+            <action>
+               seqArtifact = art.createArtifact(sprintf('%s.jast', seqArtifact.getRelativePath()))
+            </action>
+            <rule>!java.persistJavaFile(seqArtifact)
+               <action>printfln("Failed to save JAST for %s", seqArtifact.getRelativePath())</action>
             </rule>
          </rule>
       </ascent-rules>
@@ -292,8 +301,11 @@
                <action>
                   seqFile = sprintf("%s%s%s_Sequences.java", this.getAnnotation("path"), dbName, fileSep)
                </action>
-               <rule>!java.createJavaFile(seqFile, true, false)
-                  <action>throwException(sprintf("Error creating java AST: %s", seqFile))</action>
+               <action>seqArtifact = art.createArtifact(seqFile)</action>
+               <rule>!java.createJavaFile(seqArtifact, true, false)
+                  <action>
+                     throwException(sprintf("Error creating java AST: %s", seqArtifact.getRelativePath()))
+                  </action>
                </rule>
                
                <action>sequencesAst = getAst(createJavaAst(java.kw_enum, "_Sequences", javaRootId))</action>
@@ -405,7 +417,7 @@
             <rule>!isRuntimeConfig()
                <action>view = !permanent and getNoteBoolean("tt_view")</action>
                <rule>!view
-                  <action>execLib('persistJast', javaFile)</action>
+                  <action>execLib('persistJast', javaArtifact)</action>
                </rule>
             </rule>
          </rule>

=== modified file 'rules/schema/java_pojo.xml'
--- old/rules/schema/java_pojo.xml	2022-08-10 20:01:47 +0000
+++ new/rules/schema/java_pojo.xml	2025-05-09 07:59:16 +0000
@@ -3,10 +3,11 @@
 ** Module   : java_dmo.xml
 ** Abstract : walks data model AST and generates Java AST for each class node
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 OM  20220707 First commit with basic implementation.
+** 002 DDF 20250423 Use artifacts instead of filenames.
 -->
 
 <!--
@@ -71,6 +72,7 @@
    <worker class="com.goldencode.p2j.uast.JavaPatternWorker" namespace="java" />
    <worker class="com.goldencode.p2j.pattern.TemplateWorker" namespace="tpl" />
    <worker class="com.goldencode.p2j.schema.SchemaWorker"    namespace="schema" />
+   <worker class="com.goldencode.artifacts.ArtifactWorker"   namespace="art" />
    
    <!-- function library includes -->
    <include name="schema/dmo_common" />
@@ -90,6 +92,7 @@
    <variable name="ctorAst"      type="com.goldencode.ast.Aast" />
    <variable name="fieldRoot"    type="com.goldencode.ast.Aast" />
    <variable name="methRoot"     type="com.goldencode.ast.Aast" />
+   <variable name="javaArtifact" type="com.goldencode.artifacts.Artifact" />
    
    <!-- pipeline of rule-sets to process -->
    
@@ -198,9 +201,10 @@
                javaFile = sprintf("%s%s%s%s.java",
                                   parent.getAnnotation("pojo-path"), dbName, fileSep, className)
             </action>
+            <action>javaArtifact = art.createArtifact(javaFile)</action>
             
             <!-- create java file; generates AST root for java class/compile unit -->
-            <rule>!java.createJavaFile(javaFile, true, isRuntimeConfig())
+            <rule>!java.createJavaFile(javaArtifact, true, isRuntimeConfig())
                <action>throwException(sprintf("Error creating java AST: %s", javaFile))</action>
             </rule>
             
@@ -369,7 +373,7 @@
                   </rule>
                </while>
                
-               <action>execLib("persistJast", javaFile)</action>
+               <action>execLib("persistJast", javaArtifact)</action>
             </rule>
          </rule>
       </ascent-rules>

=== modified file 'rules/schema/p2o_common.rules'
--- old/rules/schema/p2o_common.rules	2017-04-01 23:33:34 +0000
+++ new/rules/schema/p2o_common.rules	2025-05-09 07:59:16 +0000
@@ -3,7 +3,7 @@
 ** Module   : p2o_common.xml
 ** Abstract : common rules used from multiple rulesets within P2O conversion
 **
-** Copyright (c) 2005-2017, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
 ** 001 ECF 20051013   @23083 Good working version. Common rules used from
@@ -13,6 +13,7 @@
 ** 003 GES 20070720   @34640 Removed the terse flag from AST persistence.
 ** 004 SVL 20130624          Added support for server runtime mode.
 ** 005 ECF 20160216          Clean up.
+** 006 DDF 20250423          Replace file with artifact.
 -->
 <!--
 ** This program is free software: you can redistribute it and/or modify
@@ -102,7 +103,7 @@
       
          <!-- save the modified progress schema AST -->
          <rule>!isRuntimeConfig()
-            <action>persist(file, true, false)</action>
+            <action>persist(artifact, true, false)</action>
          </rule>
          
       </post-rules>

=== modified file 'rules/schema/super_iface.rules'
--- old/rules/schema/super_iface.rules	2020-09-06 23:15:41 +0000
+++ new/rules/schema/super_iface.rules	2025-05-09 07:59:16 +0000
@@ -3,13 +3,14 @@
 ** Module   : super_iface.rules
 ** Abstract : rules to generate Java DMO super interface AST
 **
-** Copyright (c) 2015-2020, Golden Code Development Corporation.
+** Copyright (c) 2015-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 OM  20150826 Created initial version. Provides creation rules for the Java file and compile
 **                  unit AST of a DMO super-interface Java AST.
 ** 002 OM  20181214 Added support for class fields.
 ** 003 OM  20200906 New ORM implementation.
+** 004 DDF 20250423 Use an artifact instead of the filename for the createCompileUnit call.
 -->
 
 <!--
@@ -111,11 +112,12 @@
                                fileSep,
                                superName)
          </action>
+         <action>javaArtifact = art.createArtifact(javaFile)</action>
          
          <!-- create compile unit, the skeleton of the JAST -->
          <action>
             classAst = execLib('createCompileUnit',
-                               javaFile,
+                               javaArtifact,
                                package,
                                superName,
                                #(boolean) parent.getAnnotation('permanent'),

=== modified file 'rules/unreachable/unreachable.xml'
--- old/rules/unreachable/unreachable.xml	2024-08-21 15:04:27 +0000
+++ new/rules/unreachable/unreachable.xml	2025-05-09 07:59:16 +0000
@@ -4,7 +4,7 @@
 ** Module   : unreachable.xml
 ** Abstract : mark reachability of the nodes in the AST tree
 **
-** Copyright (c) 2005-2024, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
 ** 001 SIY 20050719   @21723 Initial implementation. It supports most 
@@ -77,6 +77,7 @@
 ** 032 CA  20231031          A block with UNDO, THROW will mark all subsequent blocks as reachable.
 ** 033 RFB 20240819          Removed setting of 'infinite_loop' due to issues with downstream blocks not being included.
 **                           Ref. #7933.
+** 034 DDF 20250424          Replaced file with artifact.
 */
 -->
 
@@ -338,7 +339,7 @@
          <!-- report unreachable code -->
          <rule>!reachable and line != 0
             <action>
-               fprintf(fout, "# %s %d: unreachable %s\n", file, line, text)
+               fprintf(fout, "# %s %d: unreachable %s\n", artifact.getRelativePath(), line, text)
             </action>
          </rule>
          
@@ -394,7 +395,7 @@
             <rule>lookupDictionaryLong(blockId, resId) == ucwrk.res_false
                <action>deferred = true</action>
                <action>
-                  fprintf(fout, "NOTE:: loop body unreachable in %s %d\n", file, line)
+                  fprintf(fout, "NOTE:: loop body unreachable in %s %d\n", artifact.getRelativePath(), line)
                </action>
             </rule>
          </rule>
@@ -520,8 +521,8 @@
                   <action on="false">
                      fprintf(fout, 
                              "ERROR: loop with label %s not found in %s %d\n",
-                             text, 
-                             file, 
+                             text,
+                             artifact.getRelativePath(),
                              line)
                   </action>
                </rule>
@@ -555,8 +556,8 @@
                <action on="false">
                   fprintf(fout, 
                           "ERROR: Loop with label %s not found in %s %d\n",
-                          text, 
-                          file, 
+                          text,
+                          artifact.getRelativePath(),
                           line)
                </action>
             </rule>
@@ -644,8 +645,8 @@
             <action>
                fprintf(fout, 
                        "NOTE:: label %s defined in %s %d\n", 
-                       text, 
-                       file, 
+                       text,
+                       artifact.getRelativePath(),
                        line)
             </action>
             <action>
@@ -741,7 +742,11 @@
             (numImmediateChildren == 0 or 
             (!descendant(prog.kw_to, 1) and !descendant(prog.kw_while, 1)))
             <action>
-               fprintf(fout, "# %s %d: handle REPEAT loop and possible expressions WAS setting infinite loop at '%s'\n", file, line, text)
+               fprintf(fout,
+                       "# %s %d: handle REPEAT loop and possible expressions WAS setting infinite loop at '%s'\n",
+                       artifact.getRelativePath(),
+                       line,
+                       text)
             </action>
          </rule>
 
@@ -1041,7 +1046,11 @@
                lookupDictionaryLong(blockId, resId) == ucwrk.res_true
                <!-- mark infinite loops -->
                <action>
-                  fprintf(fout, "# %s %d: loops WAS setting infinite loop at '%s'\n", file, line, text)
+                  fprintf(fout,
+                          "# %s %d: loops WAS setting infinite loop at '%s'\n",
+                          artifact.getRelativePath(),
+                          line,
+                          text)
                </action>
             </rule>
 

=== added directory 'src/com/goldencode/artifacts'
=== added file 'src/com/goldencode/artifacts/Artifact.java'
--- old/src/com/goldencode/artifacts/Artifact.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/Artifact.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,173 @@
+/*
+** Module   : Artifact.java
+** Abstract : represents a single artifact in a project
+**
+** Copyright (c) 2023-2025, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description----------------------------------
+** 001 GES 20230506 First version.
+**     DDF 20250228 Modified the class into an interface.
+**     DDF 20250320 Added method for accessing the relativePath and absolutePath properties.
+**     DDF 20250327 Added getFile() method.
+**     DDF 20250407 Removed unused methods, added methods for artifact locking.
+**     DDF 20250414 Added getId() and endsWith() methods.
+**     DDF 20250430 Added getResourcePath().
+*/
+
+/*
+** 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.artifacts;
+
+import java.io.File;
+
+/**
+ * Represents a single artifact in a project.
+ */
+public interface Artifact
+{
+   /**
+    * Used to get the artifact.
+    * 
+    * @return  The artifact.
+    */
+   public Artifact get();
+   
+   /**
+    * Used to get the {@code id} of the artifact.
+    *
+    * @return  The {@code id} of the artifact.
+    */
+   public Long getId();
+
+   /**
+    * Used to get the filename relative path name of the artifact.
+    *
+    * @return  The artifact's relative path name.
+    */
+   public String getRelativePath();
+
+   /**
+    * Used to set the filename relative path name of the artifact.
+    *
+    * @param   newRelativePath
+    *          The artifact's relative path name.
+    */
+   public void setRelativePath(String newRelativePath);
+
+   /**
+    * Used to get the filename absolute path name of the artifact.
+    *
+    * @return  The artifact's absolute path name.
+    */
+   public String getAbsolutePath();
+
+   /**
+    * Used to get the filename absolute path name of the artifact.
+    *
+    * @param   newAbsolutePath
+    *          The artifact's absolute path name.
+    */
+   public void setAbsolutePath(String newAbsolutePath);
+
+   /**
+    * Used to delete the artifact.
+    */
+   public void delete();
+
+   /**
+    * Used to check if the backing file of the artifact exists.
+    */
+   public boolean exists();
+
+   /**
+    * Used to get the backing file of the artifact.
+    *
+    * @return  The backing file of the artifact.
+    */
+   public File getFile();
+
+   /**
+    * Used for locking the artifact, this method prevents any access or modification of the artifact.
+    */
+   public void lock();
+
+   /**
+    * Used for unlocking the artifact, it can only be used when the artifact is locked.
+    */
+   public void unlock();
+
+   /**
+    * Checks if the artifact is locked.
+    *
+    * @return  {@code true} if the artifact is locked, {@code false} otherwise.
+    */
+   public boolean tryLock();
+   
+   /**
+    * Check if the relative path of the artifact ends with the given extension.
+    *
+    * @param   extension
+    *          The extension that will be checked.
+    *
+    * @return  {@code true} If the relative path ends with the given extension, {@code false} otherwise.
+    */
+   public boolean endsWith(String extension);
+   
+   /**
+    * Retrieve the name use to check for a resource using the class loader.
+    *
+    * @return  Resource path that can be searched using the class loader.
+    */
+   public String getResourcePath();
+}

=== added file 'src/com/goldencode/artifacts/ArtifactCollection.java'
--- old/src/com/goldencode/artifacts/ArtifactCollection.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/ArtifactCollection.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,253 @@
+/*
+ ** Module   : ArtifactCollection.java
+ ** Abstract : represents a collection of artifacts
+ **
+ ** Copyright (c) 2025, Golden Code Development Corporation.
+ **
+ ** -#- -I- --Date-- --------------------------------Description----------------------------------
+ ** 001 DDF 20250320 First version.
+ **     DDF 20250327 Added contains() method.
+ **     DDF 20250407 The artifacts collection should not be iterated directly. Added more utility
+ **                  methods.
+ **     DDF 20250408 Removed getByName() and its dependencies.
+ */
+
+/*
+ ** 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.artifacts;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+/**
+ * Represents a collection of artifacts.
+ */
+public class ArtifactCollection
+implements Iterable<Artifact>
+{
+   /** The list of artifacts. */
+   private final List<Artifact> artifacts;
+
+   /**
+    * Basic constructor.
+    */
+   public ArtifactCollection()
+   {
+      this.artifacts = new ArrayList<>();
+   }
+
+   /**
+    * Basic constructor that initializes the collection with a given list of artifacts.
+    *
+    * @param   artifactList
+    *          A list of artifacts.
+    */
+   public ArtifactCollection(List<Artifact> artifactList)
+   {
+      this.artifacts = new ArrayList<>(artifactList);
+   }
+
+   /**
+    * Basic constructor that initializes the collection with a given artifact collection.
+    *
+    * @param   col
+    *          An artifact collection.
+    */
+   public ArtifactCollection(ArtifactCollection col)
+   {
+      this.artifacts = col.getArtifacts();
+   }
+
+   /**
+    * The iterator for the collection of artifacts. The iteration is independent of the original
+    * {@link #artifacts} collection, allowing new elements to be added/removed in the original collection.
+    *
+    * @return  An iterator for the artifact collection.
+    */
+   public Iterator<Artifact> iterator()
+   {
+      return getArtifacts().iterator();
+   }
+   
+   public ListIterator<Artifact> listIterator()
+   {
+      return getArtifacts().listIterator();
+   }
+
+   /**
+    * Size of the {@link #artifacts} collection.
+    *
+    * @return  The size of the {@link #artifacts} collection.
+    */
+   public int size()
+   {
+      return artifacts.size();
+   }
+
+   /**
+    * Used to get the {@link #artifacts} collection.
+    *
+    * @return  A mutable shallow copy of the {@link #artifacts} list.
+    */
+   public List<Artifact> getArtifacts()
+   {
+      return new ArrayList<>(artifacts);
+   }
+
+   /**
+    * Gets a single element from the {@link #artifacts} collection.
+    *
+    * @param   index
+    *          The index of the element that is retrieved.
+    *
+    * @return  The artifact for the given position.
+    */
+   public Artifact get(int index)
+   {
+      return artifacts.get(index);
+   }
+
+   /**
+    * Given an artifact, add it to the collection.
+    *
+    * @param   artifact
+    *          Teh artifact that needs to be added to the collection.
+    */
+   public void add(Artifact artifact)
+   {
+      artifacts.add(artifact);
+   }
+
+   /**
+    * Checks if there are any artifacts available in the collection.
+    *
+    * @return  {@code true} if there are artifacts, {@code false} otherwise.
+    */
+   public boolean isEmpty()
+   {
+      return artifacts.isEmpty();
+   }
+
+   /**
+    * Clears the collection of artifacts.
+    */
+   public void clear()
+   {
+      artifacts.clear();
+   }
+
+   /**
+    * Given another artifacts collection, add its artifacts to the current one.
+    *
+    * @param   col
+    *          An artifact collection.
+    */
+   public void addAll(ArtifactCollection col)
+   {
+      artifacts.addAll(col.getArtifacts());
+   }
+   
+   public void addAll(Set<Artifact> artifactSet)
+   {
+      artifacts.addAll(artifactSet);
+   }
+
+   /**
+    * Check if an artifact exists in the {@link #artifacts} collection.
+    *
+    * @param   artifact
+    *          The artifact that is checked.
+    *
+    * @return  {@code true} if the artifact can be found in the {@link #artifacts},
+    *          {@code false} otherwise.
+    */
+   public boolean contains(Artifact artifact)
+   {
+      return artifacts.contains(artifact);
+   }
+
+   /**
+    * Removes an artifact from the {@link #artifacts} collection.
+    *
+    * @param   artifact
+    *          The artifact that needs to be removed.
+    *
+    * @return  {@code true} if the artifact was removed successfully, {@code false} otherwise.
+    */
+   public boolean remove(Artifact artifact)
+   {
+      return artifacts.remove(artifact);
+   }
+   
+   public void removeAll(Set<Artifact> artifactSet)
+   {
+      artifacts.removeAll(artifactSet);
+   }
+
+   /**
+    * Given a comparator, sort the {@link #artifacts} collection.
+    *
+    * @param   comparator
+    *          The comparator used for sorting.
+    */
+   public void sort(Comparator<Artifact> comparator)
+   {
+      artifacts.sort(comparator);
+   }
+}
+

=== added file 'src/com/goldencode/artifacts/ArtifactConversionResult.java'
--- old/src/com/goldencode/artifacts/ArtifactConversionResult.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/ArtifactConversionResult.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,129 @@
+/*
+ ** Module   : ArtifactConversionResult.java
+ ** Abstract : Conversion result wrapper.
+ **
+ ** Copyright (c) 2025, Golden Code Development Corporation.
+ **
+ ** -#- -I- --Date-- --------------------------------Description----------------------------------
+ ** 001 DDF 20250407 Created initial version.
+ */
+
+/*
+ ** 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.artifacts;
+
+/**
+ * Represents a conversion result, it is expected that the {@link #code} will have the value {@code 1}
+ * when the artifacts are processed successfully. The resulted {@link #artifactCollection} will be empty
+ * when the processing fails.
+ */
+public final class ArtifactConversionResult
+{
+   /** The conversion output code, {@code 1} means that the conversion was successful. */
+   private final int code;
+   
+   /** Conversion status. {@code true} when the @{@link #code} is {@code 1}, {@code false otherwise}. */
+   private final boolean failed;
+   
+   /**
+    * The returned artifact collection after being processed for conversion.
+    * The collection can be empty.
+    */
+   private final ArtifactCollection artifactCollection;
+   
+   /**
+    * Basic constructor.
+    *
+    * @param   code
+    *          The conversion output code.
+    * @param   artifactCollection
+    *          The processed collection of artifacts, the collection can be empty.
+    */
+   public ArtifactConversionResult(int code, ArtifactCollection artifactCollection)
+   {
+      this.code = code;
+      this.failed = code != 1;
+      this.artifactCollection = artifactCollection;
+   }
+   
+   /**
+    * Used to retrieve the output conversion code.
+    *
+    * @return  The output conversion code.
+    */
+   public int getCode()
+   {
+      return code;
+   }
+   
+   /**
+    * Used to retrieve the artifact processing status.
+    *
+    * @return  The artifact processing status.
+    */
+   public boolean isFailed()
+   {
+      return failed;
+   }
+   
+   /**
+    * Used to retrieve the processed artifact collection.
+    *
+    * @return  The processed artifact collection, it can be empty.
+    */
+   public ArtifactCollection getArtifactCollection()
+   {
+      return artifactCollection;
+   }
+}
\ No newline at end of file

=== added file 'src/com/goldencode/artifacts/ArtifactManager.java'
--- old/src/com/goldencode/artifacts/ArtifactManager.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/ArtifactManager.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,881 @@
+/*
+** Module   : ArtifactsManager.java
+** Abstract : manages source, intermediate and output artifacts for a project
+**
+** Copyright (c) 2023-2025, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description----------------------------------
+** 001 GES 20230506 First version.
+**     DDF 20250228 Added utility methods to access/modify/delete artifacts.
+**     DDF 20250320 Added more utility methods, improved existent ones.
+**     DDF 20250327 Added more utility methods, refactored conversion artifacts.
+**     DDF 20250407 Integrated ScanDriver, added methods for creating artifacts for lexer,
+**                  improved conversion artifact processing, refactored methods.
+**     DDF 20250408 Allow SourceArtifact duplicates of BaseArtifact instance for the moment,
+**                  replaced usage of direct filename.
+**     DDF 20250414 Added more utility methods and a wrapper that uses a functional interface
+**                  to resolve programs.
+*/
+
+/*
+** 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.artifacts;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.Constructor;
+import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+
+import com.goldencode.ast.AstManager;
+import com.goldencode.p2j.cfg.Configuration;
+import com.goldencode.p2j.cfg.ConfigurationException;
+import com.goldencode.p2j.convert.db.ConversionData;
+import com.goldencode.p2j.pattern.FileOperationsWorker;
+import com.goldencode.p2j.uast.AstGenerator;
+import com.goldencode.p2j.uast.SymbolResolver;
+import com.goldencode.p2j.util.logging.CentralLogger;
+
+/**
+ * Provides services to manage artifacts in a project.
+ */
+public class ArtifactManager
+{
+   /** Logger */
+   private static final CentralLogger LOG = CentralLogger.get(ArtifactManager.class);
+
+   /** Form in which to obtain the list of files. */
+   public enum ListType { CMD_LINE, WHITELIST, BLACKLIST, FILESPEC, FILESET }
+
+   /** List of stored artifacts. */
+   private static final List<Artifact> artifacts = new ArrayList<>();
+
+   /** Map of artifacts based on the relative path name of the artifact. */
+   private static final Map<String, Artifact> namesToArtifacts = new HashMap<>();
+   
+   /** ID counter */
+   private static final AtomicLong counter = new AtomicLong(1);
+
+   /**
+    * Create conversion artifacts based on the provided arguments.
+    *
+    * @param   args
+    *          List of command line arguments.
+    * @param   type
+    *          Type representing the form in which the list of files is obtained.
+    * @param   recursion
+    *          Specifies if matching filenames in all subdirectories should
+    *          be included.
+    * @param   help
+    *          Help text for syntax display.
+    *
+    * @return  An {@code ArtifactCollectionResult} instance containing the status code obtained
+    *          when creating the artifacts and the respective artifact collection.
+    */
+   public static ArtifactConversionResult createConversionArtifacts(String[] args,
+                                                                    ListType type,
+                                                                    boolean recursion,
+                                                                    String[] help)
+   {
+      FileList files = null;
+      switch (type)
+      {
+         case CMD_LINE:
+            ArtifactCollection col = null;
+            if ((args == null || args.length == 0) && Configuration.getArtifactCol() != null)
+            {
+               // no explicit command-line files are provided and p2j.cfg.xml has configured file-set
+               col = Configuration.getArtifactCol();
+            }
+            
+            if (col == null || col.isEmpty())
+            {
+               // there must be at least 1 arg left
+               if (args == null || args.length == 0)
+               {
+                  LOG.log(Level.SEVERE, "Missing file list parameter.");
+                  
+                  if (help != null)
+                  {
+                     LOG.log(Level.WARNING, String.join(System.lineSeparator(), help));
+                  }
+
+                  return new ArtifactConversionResult(-7, new ArtifactCollection());
+               }
+            }
+
+            return new ArtifactConversionResult(1, col);
+
+         case WHITELIST:
+            // there must be exactly 1 arg left
+            if (args == null || args.length != 1)
+            {
+               LOG.log(Level.SEVERE, "Missing custom file list parameter.");
+               
+               if (help != null)
+               {
+                  LOG.log(Level.WARNING, String.join(System.lineSeparator(), help));
+               }
+
+               return new ArtifactConversionResult(-8, new ArtifactCollection());
+            }
+            
+            files = FileListFactory.createExplicit(args[0]);
+            break;
+            
+         case FILESPEC:
+            // there must be exactly 2 args left
+            if (args == null || args.length != 2)
+            {
+               LOG.log(Level.SEVERE, "Missing/incomplete filespec parameter.");
+               
+               if (help != null)
+               {
+                  LOG.log(Level.WARNING, String.join(System.lineSeparator(), help));
+               }
+
+               return new ArtifactConversionResult(-9, new ArtifactCollection());
+            }
+            
+            files = new FileSpecList(new File(args[0]), args[1], recursion);
+            break;
+            
+         case BLACKLIST:
+            // there must be exactly 3 args left: starting directory, filespec, ignore filename
+            if (args == null || args.length != 3)
+            {
+               LOG.log(Level.SEVERE, "Missing/incomplete filespec and exclude list parameters.");
+               
+               if (help != null)
+               {
+                  LOG.log(Level.WARNING, String.join(System.lineSeparator(), help));
+               }
+
+               return new ArtifactConversionResult(-10, new ArtifactCollection());
+            }
+            
+            // TODO: we could set case-sensitivity from the legacy system setting, which would allow us to
+            //       read blacklists made with inputs on case-insensitive systems
+            files = FileListFactory.createBlacklist(args[0],
+                                                    args[1],
+                                                    recursion, 
+                                                    false, 
+                                                    args[2]);
+            break;
+         case FILESET:
+            // there must be exactly 1 arg left
+            if (args == null || args.length != 1)
+            {
+               LOG.log(Level.SEVERE, "Missing file set parameter.");
+               
+               if (help != null)
+               {
+                  LOG.log(Level.WARNING, String.join(System.lineSeparator(), help));
+               }
+
+               return new ArtifactConversionResult(-11, new ArtifactCollection());
+            }
+
+            FileSet fileSet = FileListFactory.loadFileSet(args[0]);
+            if (fileSet != null)
+            {
+               try
+               {
+                  files = FileListFactory.processFileSet(fileSet, Configuration.isCaseSensitive());
+               } 
+               catch (IOException e)
+               {
+                  LOG.log(Level.SEVERE, "Processing file set:", e);
+                  return new ArtifactConversionResult(-12, new ArtifactCollection());
+               }
+            }
+            break;
+         default:
+            LOG.log(Level.SEVERE, "Invalid command line arguments provided.");
+            break;
+      }
+      
+      if (files == null)
+      {
+         LOG.log(Level.SEVERE, "No file list available for processing.");
+         return new ArtifactConversionResult(1, new ArtifactCollection());
+      }
+
+      List<Artifact> conversionArtifacts = new ArrayList<>();
+      File[] filesData = files.list();
+      if (filesData.length == 0)
+      {
+         LOG.log(Level.SEVERE, "No files found in the file set.");
+         return new ArtifactConversionResult(1, new ArtifactCollection());
+      }
+
+      for (int i = 0; i < filesData.length; i++)
+      {
+         Artifact artifact = addArtifact(filesData[i].toString());
+         conversionArtifacts.add(artifact);
+      }
+
+      return new ArtifactConversionResult(1, new ArtifactCollection(conversionArtifacts));
+   }
+
+   /**
+    * Takes the artifacts from a {@code ArtifactCollection} instance and creates a new instance with files
+    * that use a provided suffix. There's an option to check if the file used to convert the artifacts
+    * exists.
+    *
+    * @param   toConvert
+    *          The {@code ArtifactCollection} instance containing the artifacts that need to be converted.
+    * @param   suffix
+    *          The suffix appended to artifact filenames.
+    * @param   shouldExist
+    *          {@code true} if the backing file should exist before converting the artifact, {@code false}
+    *          otherwise.
+    *
+    * @return  A new {@code ArtifactCollection} instance.
+    */
+   public static ArtifactCollection transformArtifactNames(ArtifactCollection toConvert,
+                                                           String suffix,
+                                                           boolean shouldExist)
+   {
+      List<Artifact> convertedArtifacts = new ArrayList<>();
+      Iterator<Artifact> it = toConvert.iterator();
+      while (it.hasNext())
+      {
+         Artifact currentArtifact = it.next();
+         String relativePath = currentArtifact.getRelativePath();
+         String absolutePath = currentArtifact.getAbsolutePath();
+
+         String astFile = relativePath;
+         if (shouldExist)
+         {
+            try
+            {
+               astFile = FileOperationsWorker.findFile(relativePath, null);
+            }
+            catch (ConfigurationException e)
+            {
+               LOG.log(Level.WARNING, "Could not resolve program for file (1): " + relativePath);
+            }
+
+            if (astFile == null)
+            {
+               LOG.log(Level.WARNING, "Could not resolve program for file (2): " + relativePath);
+               continue;
+            }
+         }
+
+         String newRelativePath = astFile + suffix;
+         String newAbsolutePath = absolutePath + suffix;
+         Artifact newArtifact = addArtifact(newRelativePath, newAbsolutePath);
+         convertedArtifacts.add(newArtifact);
+      }
+
+      return new ArtifactCollection(convertedArtifacts);
+   }
+
+   /**
+    * Takes the artifacts from a {@code ArtifactCollection} instance and creates a new instance with files
+    * that use a provided suffix. There's an option to check if the file used to convert the artifacts
+    * exists.
+    *
+    * @param   toConvert
+    *          The {@code ArtifactCollection} instance containing the artifacts that need to be converted.
+    * @param   suffix
+    *          The suffix appended to artifact filenames.
+    * @param   shouldExist
+    *          {@code true} if the backing file should exist before converting the artifact, {@code false}
+    *          otherwise.
+    *
+    * @return  An {@code ArtifactCollection} instance containing the artifacts representing the provided
+    *          filenames.
+    */
+   public static ArtifactCollection transformArtifactNames(List<String> toConvert,
+                                                           String suffix,
+                                                           boolean shouldExist)
+   {
+      List<Artifact> convertedArtifacts = new ArrayList<>();
+      Iterator<String> it = toConvert.iterator();
+      while (it.hasNext())
+      {
+         String file = it.next();
+         String relativePath = Configuration.normalizeFilename(file);
+         String absolutePath = Configuration.absoluteFilename(file);
+
+         String astFile = relativePath;
+         if (shouldExist)
+         {
+            try
+            {
+               astFile = FileOperationsWorker.findFile(relativePath, null);
+            }
+            catch (ConfigurationException e)
+            {
+               LOG.log(Level.WARNING, "Could not resolve program for file (1): " + relativePath);
+            }
+
+            if (astFile == null)
+            {
+               LOG.log(Level.WARNING, "Could not resolve program for file (2): " + relativePath);
+               continue;
+            }
+         }
+
+         String newRelativePath = astFile + suffix;
+         String newAbsolutePath = absolutePath + suffix;
+         Artifact newArtifact = addArtifact(newRelativePath, newAbsolutePath);
+         convertedArtifacts.add(newArtifact);
+      }
+
+      return new ArtifactCollection(convertedArtifacts);
+   }
+   
+   /**
+    * Used to resolve a list of programs with a given lambda. The lambda can use FWD specific methods
+    * which by default, should not be made available in the {@link ArtifactManager}.
+    *
+    * @param   programs
+    *          The programs that will be resolved by the lambda.
+    * @param   op
+    *          The lambda used to resolve the programs.
+    *
+    * @return  An {@link ArtifactCollection} instance containing the artifacts for the resolved programs.
+    */
+   public static ArtifactCollection createArtifacts(List<String> programs, ArtifactResolver<String> op)
+   {
+      List<Artifact> createdArtifacts = new ArrayList<>();
+      Iterator<String> it = programs.iterator();
+      while (it.hasNext())
+      {
+         String program = it.next();
+         String resolvedProgram = op.resolve(program);
+         if (resolvedProgram != null)
+         {
+            String absolutePath = Configuration.absoluteFilename(program);
+            Artifact artifact = addArtifact(program, absolutePath);
+            createdArtifacts.add(artifact);
+         }
+      }
+      return new ArtifactCollection(createdArtifacts);
+   }
+   
+   /**
+    * Creates a lexer artifact.
+    * TODO: find a way to create a general method for creating the artifact, or work on a different artifact.
+    * TODO: there is a scenario when a BaseArtifact already exists when creating a SourceArtifact. MUST FIX
+    *
+    * @param   filename
+    *          The filename of the artifact that will be created.
+    *
+    * @return  The artifact instance created.
+    */
+   private static SourceArtifact addLexerArtifact(String filename)
+   {
+      String relativePath = Configuration.normalizeFilename(filename);
+      String absolutePath = Configuration.absoluteFilename(filename);
+      
+      Long id = counter.getAndIncrement();
+      SourceArtifact artifact = new SourceArtifact(id, relativePath, absolutePath);
+      artifacts.add(artifact);
+      
+      return artifact;
+   }
+   
+   /**
+    * Creates a lexer artifact.
+    * TODO: find a way to create a general method for creating the artifact, or work on a different artifact.
+    *
+    * @param   filename
+    *          The filename of the artifact that will be created.
+    * @param   reader
+    *          The reader that will be associated with the SourceArtifact.
+    *
+    * @return  The artifact instance created.
+    */
+   public static SourceArtifact addLexerArtifact(String filename, Reader reader)
+   {
+       SourceArtifact artifact = addLexerArtifact(filename);
+       artifact.setReader(reader);
+       return artifact;
+   }
+
+   /**
+    * Creates an artifact for the provided filename.
+    *
+    * @param   filename
+    *          The relative path name of the file.
+    *
+    * @return  An {@code Artifact} instance.
+    */
+   public static Artifact addArtifact(String filename)
+   {
+      String relativePath = Configuration.normalizeFilename(filename);
+      String absolutePath = Configuration.absoluteFilename(filename);
+      return addArtifact(relativePath, absolutePath);
+   }
+   
+   /**
+    * Create an artifact for the provided filename.
+    * 
+    * @param   relativePath
+    *          The relative path name of the file.
+    * @param   absolutePath
+    *          The absolute path name of the file.
+    *
+    * @return  An {@code Artifact} instance.
+    */
+   private static Artifact addArtifact(String relativePath, String absolutePath)
+   {
+      Artifact currentArtifact = namesToArtifacts.get(relativePath);
+      if (currentArtifact != null)
+      {
+         return currentArtifact;
+      }
+      
+      Long id = counter.getAndIncrement();
+      Artifact artifact = new BaseArtifact(id, relativePath, absolutePath);
+      artifacts.add(artifact);
+      namesToArtifacts.put(relativePath, artifact);
+      
+      return artifact;
+   }
+   
+   /**
+    * An alternate artifact is built on an already existent artifact (the original), but with an additional
+    * extension. This method is used to retrieve the original artifact by removing the given extension and
+    * creating a new artifact.
+    *
+    * @param   artifact
+    *          The alternate artifact.
+    * @param   extensionToRemove
+    *          The extension that needs to be removed in order to retrieve the original artifact.
+    *
+    * @return  If the given artifact does not end with the given extension, {@code null} is returned.
+    *          When there is no extension in the remaining path after removing the given extension,
+    *          then there is no original artifact available and in this case, {@code null} is returned.
+    *          In all other scenarios, the original artifact is returned.
+    */
+   public static Artifact getOriginalArtifact(Artifact artifact, String extensionToRemove)
+   {
+      if (!artifact.endsWith(extensionToRemove))
+      {
+         LOG.log(Level.WARNING, "There is no original artifact for " + artifact.getRelativePath());
+         return null;
+      }
+      
+      String relativePath = artifact.getRelativePath();
+      int len = relativePath.length() - extensionToRemove.length();
+      String newRelativePath = relativePath.substring(0, len);
+      if (newRelativePath.indexOf('.') == -1)
+      {
+         LOG.log(Level.WARNING, "No existent alternate relative path exists for " + relativePath);
+         return null;
+      }
+      
+      return addArtifact(newRelativePath);
+   }
+   
+   /**
+    * Retrieve all artifacts available.
+    * 
+    * @return  A mutable shallow copy of the {@link #artifacts} list.
+    */
+   public static List<Artifact> getArtifacts()
+   {
+      return new ArrayList<>(artifacts);
+   }
+   
+   /**
+    * Retrieve a list of artifacts that match the provided filter. The filter should only be an
+    * {@link Artifact} class type.
+    * 
+    * @param   clazz
+    *          The class that will be used as a filter.
+    *          
+    * @return  An {code ArtifactCollection} instance containing the artifacts that match the provided filter.
+    */
+   public static ArtifactCollection getArtifactByFilter(Class<?> clazz)
+   {
+      List<Artifact> filteredArtifacts = new ArrayList<>();
+      Iterator<Artifact> it = artifacts.iterator();
+      while (it.hasNext())
+      {
+         Artifact artifact = it.next();
+         if (artifact.getClass().equals(clazz))
+         {
+            filteredArtifacts.add(artifact);
+         }
+      }
+
+      return new ArtifactCollection(filteredArtifacts);
+   }
+
+   /**
+    * Used to iterate over a {@code ArtifactCollection} instance and retrieve all relative path names
+    * of the available artifacts.
+    *
+    * @param   col
+    *          The {@code ArtifactCollection} instance.
+    *
+    * @return  A list containing the relative path names of the artifacts.
+    */
+   public static List<String> getArtifactsRelativePath(ArtifactCollection col)
+   {
+      List<String> relativeNames = new ArrayList<>();
+      Iterator<Artifact> it = col.iterator();
+      while (it.hasNext())
+      {
+         Artifact artifact = it.next();
+         if (artifact.exists())
+         {
+            relativeNames.add(artifact.getRelativePath());
+         }
+      }
+      
+      return relativeNames;
+   }
+
+   /**
+    * Used to retrieve a collection of artifacts based on the provided list of names.
+    *
+    * @param   names
+    *          The list of names for which the artifact collection needs to be created.
+    *
+    * @return  An {@code ArtifactCollection} instance.
+    */
+   public static ArtifactCollection getArtifactsByName(List<String> names)
+   {
+      List<Artifact> filteredArtifacts = new ArrayList<>();
+      Iterator<String> it = names.iterator();
+      while (it.hasNext())
+      {
+         String name = it.next();
+         Artifact artifact = namesToArtifacts.get(name);
+         if (artifact == null)
+         {
+            //TODO: not ok
+            continue;
+         }
+         
+         filteredArtifacts.add(artifact);
+      }
+      
+      return new ArtifactCollection(filteredArtifacts);
+   }
+
+   /**
+    * Used to retrieve a collection of artifacts based on the provided set of names.
+    *
+    * @param   names
+    *          The list of names for which the artifact collection needs to be created.
+    *
+    * @return  An {@code ArtifactCollection} instance.
+    */
+   public static ArtifactCollection getArtifactsByName(Set<String> names)
+   {
+      return getArtifactsByName(new ArrayList<>(names));
+   }
+   
+   /**
+    * Deletes all artifacts.
+    */
+   public static void deleteArtifacts()
+   {
+      artifacts.clear();
+   }
+
+   /**
+    * Used to delete existent files for artifacts based on each provided extension.
+    *
+    * @param   col
+    *          The collection of artifacts.
+    * @param   extslist
+    *          The extensions list.
+    */
+   public static void zapFiles(ArtifactCollection col, String[] extslist)
+   {
+      Iterator<Artifact> it = col.iterator();
+      while (it.hasNext())
+      {
+         Artifact artifact = it.next();
+         for (int j = 0; j < extslist.length; j++)
+         {
+            File check = new File(artifact.getRelativePath() + extslist[j]);
+            
+            if (check.exists() && check.isFile())
+            {
+               check.delete();
+            }
+         }
+      }
+   }
+
+   /**
+    * Used as a centralized point of creating artifact collections through the provided class.
+    * The class must be a {@code FileList} instance and the arguments should match an existent
+    * constructor.
+    *
+    * @param   clazz
+    *          A {@code FileList} instance.
+    * @param   args
+    *          The arguments for the constructor.
+    *
+    * @return  An {@code ArtifactCollection} instance resulted from the {@code FileList} instance.
+    */
+   public static ArtifactCollection createArtifactsByFileList(Class<?> clazz, Object... args)
+   {
+      Constructor<?>[] constructors = clazz.getConstructors();
+      Constructor<?> matchingConstructor = null;
+      
+      for (int i = 0; i < constructors.length; i++)
+      {
+         Class<?>[] paramTypes = constructors[i].getParameterTypes();
+         if (paramTypes.length != args.length)
+         {
+            continue;
+         }
+         
+         boolean match = true;
+         for (int j = 0; j < paramTypes.length; j++)
+         {
+            Class<?> expected = paramTypes[j];
+            Class<?> actual = args[j].getClass();
+            
+            if (expected == boolean.class && actual == Boolean.class)
+            {
+               continue;
+            }
+            
+            if (!expected.isAssignableFrom(actual))
+            {
+               match = false;
+               break;
+            }
+         }
+         
+         if (match)
+         {
+            matchingConstructor = constructors[i];
+            break;
+         }
+      }
+      
+      if (matchingConstructor == null)
+      {
+         LOG.log(Level.SEVERE, "No matching constructor found.");
+         return null;
+      }
+      
+      Object[] convertedArgs = new Object[args.length];
+      for (int i = 0; i < args.length; i++) 
+      {
+          if (args[i] instanceof Boolean)
+          {
+              convertedArgs[i] = (boolean) args[i];
+          } 
+          else 
+          {
+              convertedArgs[i] = args[i];
+          }
+      }
+
+      FileList files;
+      try
+      {
+         files = (FileList) matchingConstructor.newInstance(convertedArgs);
+      }
+      catch (Exception e)
+      {
+         LOG.log(Level.SEVERE, "Could not instantiate FileList object:", e);
+         return null;
+      }
+      
+      File[] filesArray = files.list();
+      if (filesArray.length == 0)
+      {
+         LOG.log(Level.WARNING, "No files found in the file set.");
+      }
+      
+      List<Artifact> resolvedArtifacts = new ArrayList<>();
+      for (int i = 0; i < filesArray.length; i++)
+      {
+         Artifact artifact = addArtifact(filesArray[i].toString());
+
+         resolvedArtifacts.add(artifact);
+      }
+      
+      return new ArtifactCollection(resolvedArtifacts);
+   }
+
+   /**
+    * Used to merge the artifacts of two or more {@code ArtifactCollection}.
+    *
+    * @param   colList
+    *          The list containing {@code ArtifactCollection} instances.
+    *
+    * @return  A new {@code ArtifactCollection} instances containing all artifacts from the list.
+    */
+   public static ArtifactCollection mergeArtifactCollections(List<ArtifactCollection> colList)
+   {
+      List<Artifact> l = new ArrayList<>();
+      Iterator<ArtifactCollection> it = colList.iterator();
+      while (it.hasNext())
+      {
+         ArtifactCollection a = it.next();
+         l.addAll(a.getArtifacts());
+      }
+      return new ArtifactCollection(l);
+   }
+
+   /**
+    * Given an artifact collection, delete all the artifacts.
+    *
+    * @param   col
+    *          The collection of artifacts that must be deleted.
+    */
+   public static void deleteArtifacts(ArtifactCollection col)
+   {
+      if (col.isEmpty())
+      {
+         return;
+      }
+
+      Iterator<Artifact> it = col.iterator();
+      while (it.hasNext())
+      {
+         Artifact artifact = it.next();
+         artifact.delete();
+      }
+   }
+
+   /**
+    * Clean all records about this artifact, from the incremental conversion database.
+    * <p>
+    * This will clear all work tables which have records for this artifact AST ID, and all
+    * records from the <code>CLASS_DEFINITIONS</code> table.
+    *
+    * @param    artifact
+    *           The artifact to clear.
+    *
+    * @return   An artifact collection containing a list of dependency files.
+    */
+   public static ArtifactCollection cleanArtifact(Artifact artifact)
+   {
+      Set<String> files = ConversionData.clean(artifact.getRelativePath());
+      if (files.isEmpty())
+      {
+         return new ArtifactCollection();
+      }
+
+      return getArtifactsByName(files);
+   }
+
+   /**
+    * Resolve the file set, by using the initial files (loaded from the reference directory or explicit file
+    * names), and applying the Include and eXclude operations. This method creates an artifact collection
+    * based on the resolved file set.
+    *
+    * @param    fileSet
+    *           The file set.
+    * @param    caseSens
+    *           Flag indicating if the legacy operating system is case-sensitive or not.
+    *
+    * @return   An artifact collection.
+    *
+    * @throws   IOException
+    *           In case of problems reading files from disk.
+    */
+   public static ArtifactCollection processFileSet(FileSet fileSet, boolean caseSens)
+   throws IOException
+   {
+      FileList fl = FileListFactory.processFileSet(fileSet, caseSens);
+      String[] files = fl.listFilenames();
+
+      ArtifactCollection col = new ArtifactCollection();
+      for (int i = 0; i < files.length; i++)
+      {
+         Artifact a = addArtifact(files[i]);
+         col.add(a);
+      }
+      return col;
+   }
+
+   /**
+    * Given an artifact collection and a filename, check if there is any artifact
+    * available for the filename.
+    *
+    * @param   col
+    *          The artifact collection.
+    * @param   filename
+    *          The filename.
+    *
+    * @return  {@code true} if there is an artifact for the provided filename that
+    *          exists in the artifact collection, {@code false} otherwise.
+    */
+   public static boolean containsArtifact(ArtifactCollection col, String filename)
+   {
+      Artifact artifact = namesToArtifacts.get(filename);
+      if (artifact == null)
+      {
+         return false;
+      }
+
+      return col.contains(artifact);
+   }
+}
\ No newline at end of file

=== added file 'src/com/goldencode/artifacts/ArtifactResolver.java'
--- old/src/com/goldencode/artifacts/ArtifactResolver.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/ArtifactResolver.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,87 @@
+/*
+ ** Module   : ArtifactsResolver.java
+ ** Abstract : Functional interface used to wrap search implementations for programs.
+ **
+ ** Copyright (c) 2023-2025, Golden Code Development Corporation.
+ **
+ ** -#- -I- --Date-- --------------------------------Description----------------------------------
+ ** 001 DDF 20250414 First version.
+ */
+
+/*
+ ** 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.artifacts;
+
+/**
+ * Functional interface that allows the search implementation to be abstracted.
+ * Some of the search implementations rely on FWD specific methods instead
+ * of the generic filesystem processing, so this interface must be used to
+ * replace such scenarios.
+ *
+ * @param   <T>
+ *          The parameter type.
+ */
+@FunctionalInterface
+public interface ArtifactResolver<T>
+{
+   /**
+    * Resolve the given item.
+    *
+    * @param   item
+    *          The item that will be resolved.
+    *
+    * @return  The resolved result of the item.
+    */
+   String resolve(T item);
+}

=== added file 'src/com/goldencode/artifacts/ArtifactWorker.java'
--- old/src/com/goldencode/artifacts/ArtifactWorker.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/ArtifactWorker.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,101 @@
+/*
+ ** Module   : ArtifactWorker.java
+ ** Abstract : Exposes the artifact manager capabilities.
+ **
+ ** Copyright (c) 2025, Golden Code Development Corporation.
+ **
+ ** -#- -I- --Date-- --------------------------------Description----------------------------------
+ ** 001 DDF 20250423 First version.
+ */
+
+/*
+ ** 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.artifacts;
+
+import com.goldencode.p2j.convert.AbstractConversionWorker;
+
+/**
+ * A worker that provides the artifact manager capabilities in TRPL.
+ */
+public class ArtifactWorker
+extends AbstractConversionWorker
+{
+   /**
+    * Default constructor which initializes library.
+    */
+   public ArtifactWorker()
+   {
+      super();
+      setLibrary(new ArtifactLibrary());
+   }
+   
+   /**
+    * The class that exposes its public methods as worker interface in TRPL.
+    */
+   public class ArtifactLibrary
+   {
+      /**
+       * Create an artifact for the provided filename.
+       *
+       * @param   filename
+       *          The filename used to craete the artifact.
+       *
+       * @return  An artifact instance.
+       */
+      public Artifact createArtifact(String filename)
+      {
+         return ArtifactManager.addArtifact(filename);
+      }
+   }
+}

=== added file 'src/com/goldencode/artifacts/BaseArtifact.java'
--- old/src/com/goldencode/artifacts/BaseArtifact.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/BaseArtifact.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,325 @@
+/*
+** Module   : BaseArtifact.java
+** Abstract : Parent class of an artifact.
+**
+** Copyright (c) 2025, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description----------------------------------
+** 001 DDF 20250228 Created initial version.
+**     DDF 20250320 Modified constructor, added setters and getters.
+**     DDF 20250327 Added getFile() method.
+**     DDF 20250407 Removed name and extName, added artifact locking methods, refactored methods.
+**     DDF 20250414 Added endsWith() and equals().
+**     DDF 20250430 Added getResourcePath().
+*/
+
+/*
+** 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.artifacts;
+
+import com.goldencode.p2j.convert.db.ConversionData;
+
+import java.io.File;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Represents a single artifact in a project, contains the most basic data.
+ */
+public class BaseArtifact
+implements Artifact
+{
+   /** The id of the artifact. */
+   private Long id = null;
+
+   /** The relative path name of the filename used to create the artifact. */
+   private String relativePath = null;
+
+   /** The absolute path name of the filename used to create the artifact. */
+   private String absolutePath = null;
+   
+   /** Used for locking the artifact when modifying it. */
+   private final ReentrantLock lock = new ReentrantLock();
+
+   /**
+    * Main constructor.
+    *
+    * @param   id
+    *          The id of the artifact.
+    * @param   relativePath
+    *          The relative path name of the filename.
+    * @param   absolutePath
+    *          The absolute path name of the filename.
+    */
+   public BaseArtifact(Long id, String relativePath, String absolutePath)
+   {
+      this.id = id;
+      this.relativePath = relativePath;
+      this.absolutePath = absolutePath;
+   }
+
+   /**
+    * Used to get the {@code id} of the artifact.
+    *
+    * @return  The {@code id} of the artifact.
+    */
+   @Override
+   public Long getId()
+   {
+      return id;
+   }
+
+   /**
+    * Used to get the artifact.
+    * 
+    * @return  The artifact.
+    */
+   @Override
+   public BaseArtifact get()
+   {
+      return this;
+   }
+
+   /**
+    * Used to get the {@code relativePath} of the artifact.
+    *
+    * @return  The {@code relativePath} of the artifact.
+    */
+   @Override
+   public String getRelativePath()
+   {
+      return relativePath;
+   }
+
+   /**
+    * Used to set the {@code relativePath} of the artifact.
+    *
+    * @param   newRelativePath
+    *          The new relative path name of the artifact.
+    */
+   @Override
+   public void setRelativePath(String newRelativePath)
+   {
+      this.relativePath = newRelativePath;
+   }
+
+   /**
+    * Used to get the {@code absolutePath} of the artifact.
+    *
+    * @return  The {@code absolutePath} of the artifact.
+    */
+   @Override
+   public String getAbsolutePath()
+   {
+      return absolutePath;
+   }
+
+   /**
+    * Used to set the {@code absolutePath} of the artifact.
+    *
+    * @param   newAbsolutePath
+    *          The new absolute path name of the artifact.
+    */
+   @Override
+   public void setAbsolutePath(String newAbsolutePath)
+   {
+      this.absolutePath = newAbsolutePath;
+   }
+
+   /**
+    * Used to delete the artifact.
+    */
+   @Override
+   public void delete()
+   {
+      deleteFile();
+   }
+
+   /**
+    * Checks if the file backing the artifact exists.
+    *
+    * @return  {@code true} if the file exists, {@code false} otherwise.
+    */
+   @Override
+   public boolean exists()
+   {
+      File file = getFile();
+      return file.exists() && file.isFile();
+   }
+   
+   /**
+    * Retrieve the name use to check for a resource using the class loader.
+    *
+    * @return  Resource path that can be searched using the class loader.
+    */
+   @Override
+   public String getResourcePath()
+   {
+      if (relativePath.startsWith("./"))
+      {
+         return relativePath.substring(2);
+      }
+      
+      return relativePath;
+   }
+
+   /**
+    * Delete the backing file of the artifact if it exists.
+    *
+    * @return  {@code true} if the file was deleted successfully, {@code false} otherwise.
+    */
+   public boolean deleteFile()
+   {
+      File file = getFile();
+      if (file.exists() && file.isFile())
+      {
+         return file.delete();
+      }
+      return false;
+   }
+
+   /**
+    * Used to get the backing file of the artifact.
+    *
+    * @return  The backing file of the artifact.
+    */
+   @Override
+   public File getFile()
+   {
+      return new File(relativePath);
+   }
+
+   /**
+    * Check if this artifact must be converted.
+    * <p>
+    * An artifact must be converted if the artifact file is new, or itself or any of its include files
+    * (from reading the associated <code>.pphints</code> has changed).
+    * // TODO: move this to Artifact
+    *
+    * @return   <code>true</code> if this artifact has changed and must be converted.
+    */
+   public boolean mustConvert()
+   {
+      return ConversionData.mustConvertFile(relativePath);
+   }
+
+   /**
+    * Used for locking the artifact, this method prevents any access or modification of the artifact.
+    */
+   @Override
+   public void lock()
+   {
+      lock.lock();
+   }
+
+   /**
+    * Used for unlocking the artifact, it can only be used when the artifact is locked.
+    */
+   @Override
+   public void unlock()
+   {
+      lock.unlock();
+   }
+
+   /**
+    * Checks if the artifact is locked.
+    *
+    * @return  {@code true} if the artifact is locked, {@code false} otherwise.
+    */
+   @Override
+   public boolean tryLock()
+   {
+      return lock.tryLock();
+   }
+   
+   /**
+    * Check if the relative path of the artifact ends with the given extension.
+    *
+    * @param   extension
+    *          The extension that will be checked.
+    *
+    * @return  {@code true} If the relative path ends with the given extension, {@code false} otherwise.
+    */
+   @Override
+   public boolean endsWith(String extension)
+   {
+      return relativePath.endsWith(extension);
+   }
+   
+   /**
+    * Checks whether some other object is "equal to" this one.
+    *
+    * @param   obj
+    *          Another object to compare to.
+    *
+    * @return  {@code true} if this object is the same as the obj argument.
+    */
+   @Override
+   public boolean equals(Object obj)
+   {
+      if (obj == this)
+      {
+         return true;
+      }
+      
+      if (!(obj instanceof Artifact))
+      {
+         return false;
+      }
+      
+      Artifact o = (Artifact) obj;
+      return id.equals(o.getId())                     &&
+             relativePath.equals(o.getRelativePath()) &&
+             absolutePath.equals(o.getAbsolutePath());
+   }
+}

=== renamed file 'src/com/goldencode/p2j/cfg/DirectoryReference.java' => 'src/com/goldencode/artifacts/DirectoryReference.java'
--- old/src/com/goldencode/p2j/cfg/DirectoryReference.java	2022-04-16 18:38:24 +0000
+++ new/src/com/goldencode/artifacts/DirectoryReference.java	2025-05-09 07:59:16 +0000
@@ -2,11 +2,13 @@
 ** Module   : DirectoryReference.java
 ** Abstract : For a file-set configuration, this represents a directory reference.
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20220412 Created initial version.
+** 002 GES 20230524 Moved packages to be more generic.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -60,7 +62,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.cfg;
+package com.goldencode.artifacts;
 
 /**
  * For a file-set configuration, this represents a directory reference.

=== renamed file 'src/com/goldencode/p2j/cfg/ExcludeFilter.java' => 'src/com/goldencode/artifacts/ExcludeFilter.java'
--- old/src/com/goldencode/p2j/cfg/ExcludeFilter.java	2022-04-16 18:38:24 +0000
+++ new/src/com/goldencode/artifacts/ExcludeFilter.java	2025-05-09 07:59:16 +0000
@@ -2,11 +2,13 @@
 ** Module   : ExcludeFilter.java
 ** Abstract : For a file-set configuration, this represents the eXclude operation.
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20220412 Created initial version.
+** 002 GES 20230524 Moved packages to be more generic.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -60,7 +62,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.cfg;
+package com.goldencode.artifacts;
 
 /**
  * When a configuration is read from a {@link FileSet}, this represents the eXclude operation, when a set of

=== renamed file 'src/com/goldencode/p2j/util/ExplicitFileList.java' => 'src/com/goldencode/artifacts/ExplicitFileList.java'
--- old/src/com/goldencode/p2j/util/ExplicitFileList.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/artifacts/ExplicitFileList.java	2025-05-09 07:59:16 +0000
@@ -14,7 +14,9 @@
 ** 005 CA  20200116        Javadoc fixes.
 ** 006 CA  20220412        Added file-set support (either -Z command-line option or p2j.cfg.xml configuration).
 ** 007 GBB 20230512        Logging methods replaced by CentralLogger/ConversionStatus.
+** 008 GES 20230524          Moved packages to be more generic.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -68,7 +70,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.util;
+package com.goldencode.artifacts;
 
 import com.goldencode.p2j.util.logging.*;
 

=== renamed file 'src/com/goldencode/p2j/util/FileList.java' => 'src/com/goldencode/artifacts/FileList.java'
--- old/src/com/goldencode/p2j/util/FileList.java	2025-04-11 07:50:19 +0000
+++ new/src/com/goldencode/artifacts/FileList.java	2025-05-09 07:59:16 +0000
@@ -20,6 +20,7 @@
 **     CA  20220412          Added file-set support (either -Z command-line option or p2j.cfg.xml 
 **                           configuration).
 ** 010 ES  20250328          Added addAdditionalFiles and removeFiles methods to add/remove specific files.
+** 011 GES 20230524          Moved packages to be more generic.
 */
 
 /*
@@ -75,7 +76,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.util;
+package com.goldencode.artifacts;
 
 import java.io.*;
 import java.util.*;

=== renamed file 'src/com/goldencode/p2j/util/FileListFactory.java' => 'src/com/goldencode/artifacts/FileListFactory.java'
--- old/src/com/goldencode/p2j/util/FileListFactory.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/artifacts/FileListFactory.java	2025-05-09 07:59:16 +0000
@@ -9,6 +9,7 @@
 **     GES 20220323 Added explicit whitelist helper and moved common file name handling code here.
 **     CA  20220412 Added file-set support (either -Z command-line option or p2j.cfg.xml configuration).
 ** 002 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
+** 003 GES 20230524 Moved packages to be more generic.
 */
 
 /*
@@ -65,7 +66,7 @@
 */
 
 
-package com.goldencode.p2j.util;
+package com.goldencode.artifacts;
 
 import java.io.*;
 import java.nio.file.*;
@@ -75,7 +76,6 @@
 
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
-import com.goldencode.p2j.util.logging.*;
 import com.goldencode.util.*;
 
 /**

=== renamed file 'src/com/goldencode/p2j/cfg/FileReference.java' => 'src/com/goldencode/artifacts/FileReference.java'
--- old/src/com/goldencode/p2j/cfg/FileReference.java	2022-04-13 12:20:41 +0000
+++ new/src/com/goldencode/artifacts/FileReference.java	2025-05-09 07:59:16 +0000
@@ -2,11 +2,13 @@
 ** Module   : FileReference.java
 ** Abstract : For a file-set configuration, this represents a file reference.
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20220412 Created initial version.
+** 002 GES 20230524 Moved packages to be more generic.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -60,7 +62,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.cfg;
+package com.goldencode.artifacts;
 
 /**
  * For a file-set configuration, this represents a file reference.

=== renamed file 'src/com/goldencode/p2j/cfg/FileSet.java' => 'src/com/goldencode/artifacts/FileSet.java'
--- old/src/com/goldencode/p2j/cfg/FileSet.java	2022-05-01 15:40:07 +0000
+++ new/src/com/goldencode/artifacts/FileSet.java	2025-05-09 07:59:16 +0000
@@ -2,12 +2,14 @@
 ** Module   : FileSet.java
 ** Abstract : The configuration to load the file-list from a file-set.
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20220412 Created initial version.
 **     CA  20220501 Added copy constructor.
+** 002 GES 20230524 Moved packages to be more generic.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -61,7 +63,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.cfg;
+package com.goldencode.artifacts;
 
 import java.util.*;
 

=== renamed file 'src/com/goldencode/p2j/util/FileSpecList.java' => 'src/com/goldencode/artifacts/FileSpecList.java'
--- old/src/com/goldencode/p2j/util/FileSpecList.java	2023-05-24 22:36:08 +0000
+++ new/src/com/goldencode/artifacts/FileSpecList.java	2025-05-09 07:59:16 +0000
@@ -23,6 +23,7 @@
 ** 008 CA  20220412        Added file-set support (either -Z command-line option or p2j.cfg.xml configuration).
 ** 009 GBB 20230512        Logging methods replaced by CentralLogger/ConversionStatus.
 ** 010 OM  20230510        Allow unverified directories to be set as [startDir] in import mode.
+** 011 GES 20230524        Moved packages to be more generic.
 */
 
 /*
@@ -78,7 +79,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.util;
+package com.goldencode.artifacts;
 
 import java.io.*;
 import java.util.*;

=== renamed file 'src/com/goldencode/p2j/cfg/FilterOperation.java' => 'src/com/goldencode/artifacts/FilterOperation.java'
--- old/src/com/goldencode/p2j/cfg/FilterOperation.java	2022-04-16 18:38:24 +0000
+++ new/src/com/goldencode/artifacts/FilterOperation.java	2025-05-09 07:59:16 +0000
@@ -2,11 +2,13 @@
 ** Module   : FileOperation.java
 ** Abstract : For a file-set configuration, this is the base class for Include or eXclude operations.
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20220412 Created initial version.
+** 002 GES 20230524 Moved packages to be more generic.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -60,7 +62,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.cfg;
+package com.goldencode.artifacts;
 
 /**
  * When a configuration is read from a {@link FileSet}, this represents the base class for Include or eXclude

=== renamed file 'src/com/goldencode/p2j/cfg/IncludeFilter.java' => 'src/com/goldencode/artifacts/IncludeFilter.java'
--- old/src/com/goldencode/p2j/cfg/IncludeFilter.java	2022-04-16 18:38:24 +0000
+++ new/src/com/goldencode/artifacts/IncludeFilter.java	2025-05-09 07:59:16 +0000
@@ -2,11 +2,13 @@
 ** Module   : IncludeFilter.java
 ** Abstract : For a file-set configuration, this represents the Include operation.
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20220412 Created initial version.
+** 002 GES 20230524 Moved packages to be more generic.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -60,7 +62,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.cfg;
+package com.goldencode.artifacts;
 
 /**
  * When a configuration is read from a {@link FileSet}, this represents the Include operation, when a set of

=== added file 'src/com/goldencode/artifacts/IntermediateArtifact.java'
--- old/src/com/goldencode/artifacts/IntermediateArtifact.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/IntermediateArtifact.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,87 @@
+/*
+** Module   : IntermediateArtifact.java
+** Abstract : Represents a single intermediate artifact in a project.
+**
+** Copyright (c) 2025, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description----------------------------------
+** 001 DDF 20250228 Created initial version.
+**     DDF 20250320 Modified the constructor.
+*/
+
+/*
+** 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.artifacts;
+
+/**
+ * Represents a single intermediate artifact in a project.
+ */
+public class IntermediateArtifact
+extends BaseArtifact
+{
+   /**
+    * Basic constructor.
+    * 
+    * @param   id
+    *          The id of the artifact.
+    * @param   relativePath
+    *          The filename relative path name of the artifact.
+    * @param   absolutePath
+    *          The filename absolute path name of the artifact.
+    */
+   public IntermediateArtifact(Long id, String relativePath, String absolutePath)
+   {
+      super(id, relativePath, absolutePath);
+   }
+}

=== added file 'src/com/goldencode/artifacts/OutputArtifact.java'
--- old/src/com/goldencode/artifacts/OutputArtifact.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/OutputArtifact.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,87 @@
+/*
+** Module   : OutputArtifact.java
+** Abstract : Represents a single output artifact in a project.
+**
+** Copyright (c) 2025, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description----------------------------------
+** 001 DDF 20250228 Created initial version.
+**     DDF 20250320 Modified the constructor.
+*/
+
+/*
+** 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.artifacts;
+
+/**
+ * Represents a single output artifact in a project.
+ */
+public class OutputArtifact
+extends BaseArtifact
+{
+   /**
+    * Basic constructor.
+    *
+    * @param   id
+    *          The id of the artifact.
+    * @param   relativePath
+    *          The filename relative path name of the artifact.
+    * @param   absolutePath
+    *          The filename absolute path name of the artifact.
+    */
+   public OutputArtifact(Long id, String relativePath, String absolutePath)
+   {
+      super(id, relativePath, absolutePath);
+   }
+}

=== renamed file 'src/com/goldencode/p2j/cfg/PathReference.java' => 'src/com/goldencode/artifacts/PathReference.java'
--- old/src/com/goldencode/p2j/cfg/PathReference.java	2022-04-13 12:20:41 +0000
+++ new/src/com/goldencode/artifacts/PathReference.java	2025-05-09 07:59:16 +0000
@@ -2,11 +2,13 @@
 ** Module   : PathReference.java
 ** Abstract : For a file-set configuration, this represents the base class for a directory or file reference.
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20220412 Created initial version.
+** 002 GES 20230524 Moved packages to be more generic.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -60,7 +62,7 @@
 **     attribution required in this license.
 */
 
-package com.goldencode.p2j.cfg;
+package com.goldencode.artifacts;
 
 /**
  * For a file-set configuration, this represents the base class for a directory or file reference.

=== added file 'src/com/goldencode/artifacts/SourceArtifact.java'
--- old/src/com/goldencode/artifacts/SourceArtifact.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/artifacts/SourceArtifact.java	2025-05-09 07:59:16 +0000
@@ -0,0 +1,117 @@
+/*
+** Module   : SourceArtifact.java
+** Abstract : represents an artifact which is an input in a project
+**
+** Copyright (c) 2023-2025, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description----------------------------------
+** 001 GES 20230506 First version.
+**     DDF 20250228 Added constructor.
+**     DDF 20250320 Modified the constructor.
+**     DDF 20250407 Added reader property.
+*/
+
+/*
+** 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.artifacts;
+
+import java.io.*;
+
+/**
+ * Represents an artifact which is an input in a project. Source artifacts can be code, data, configuration
+ * or any other input which is provided to the project externally. This means it may be modified externally,
+ * and it should not be deleted or modified by any tools operating on the project.
+ */
+public class SourceArtifact
+extends BaseArtifact
+{
+   /** Character stream for the backing file of the artifact. */
+   private Reader reader;
+   
+   /**
+    * Basic constructor.
+    *
+    * @param   id
+    *          The id of the artifact.
+    * @param   relativePath
+    *          The filename relative path name of the artifact.
+    * @param   absolutePath
+    *          The filename absolute path name of the artifact.
+    */
+   public SourceArtifact(Long id, String relativePath, String absolutePath)
+   {
+      super(id, relativePath, absolutePath);
+   }
+   
+   /**
+    * Get the character stream for the backing file of the artifact.
+    *
+    * @return  The character stream.
+    */
+   public Reader getReader()
+   {
+      return reader;
+   }
+   
+   /**
+    * Set the character stream for the backing file of the artifact.
+    *
+    * @param   reader
+    *          The reader.
+    */
+   public void setReader(Reader reader)
+   {
+      this.reader = reader;
+   }
+}

=== modified file 'src/com/goldencode/ast/Aast.java'
--- old/src/com/goldencode/ast/Aast.java	2024-08-12 06:53:50 +0000
+++ new/src/com/goldencode/ast/Aast.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : Aast.java
 ** Abstract : extension of AST interface to support post-processing features
 **
-** Copyright (c) 2005-2024, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 ECF 20050218   @19888 Created initial version.
@@ -112,6 +112,9 @@
 ** 058 CA  20240324          Performance improvement for runtime annotations: are stored using an integer
 **                           instead of string.
 ** 059 CA  20240812          Small improvement for 'getImmediateChild' - initialize the set size.
+** 060 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250423          Added getFilename() back temporarily.
+**     DDF 20250423          Removed getFilename() after removing its usage from TRPL.
 */
 
 /*
@@ -170,6 +173,8 @@
 package com.goldencode.ast;
 
 import antlr.collections.AST;
+import com.goldencode.artifacts.Artifact;
+
 import java.util.*;
 
 /**
@@ -396,22 +401,22 @@
    public void clearIds();
    
    /**
-    * Clear all IDs of an AST and register it under a new filename. The
+    * Clear all IDs of an AST and register it under a new artifact. The
     * AST is walked to fix-up all parent-child relationships as part of
     * the re-initialization.
     * <p>
     * This would be done in order to safely save a copy AST under a
-    * different filename than the source AST. It is necessary in order to
-    * avoid having two ASTs registered to the same filename and ID in the
+    * different artifact than the source AST. It is necessary in order to
+    * avoid having two ASTs registered to the same artifact and ID in the
     * AST registry.
     *
-    * @param   filename
-    *          New filename under which the AST is registered.
+    * @param   artifact
+    *          New artifact under which the AST is registered.
     *
     * @throws  AstException
     *          if any error occurs registering AST or applying fixups.
     */
-   public void brainwash(String filename)
+   public void brainwash(Artifact artifact)
    throws AstException;
    
    /**
@@ -1424,12 +1429,12 @@
    public int getNumChildren();
    
    /**
-    * Get the name of the source file associated with this AST
+    * Get the artifact of the source file associated with this AST.
     *
-    * @return  Name of source file, or <code>null</code> if no name has yet
+    * @return  Artifact of source file, or <code>null</code> if no artifact has yet
     *          been assigned.
     */
-   public String getFilename();
+   public Artifact getArtifact();
    
    /**
     * Return a string representing the path of this AST from the root AST,

=== modified file 'src/com/goldencode/ast/AnnotatedAst.java'
--- old/src/com/goldencode/ast/AnnotatedAst.java	2024-04-23 08:14:30 +0000
+++ new/src/com/goldencode/ast/AnnotatedAst.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : AnnotatedAst.java
 ** Abstract : an AST which supports annotations and other features needed for post-processing
 **
-** Copyright (c) 2005-2024, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 ECF 20050218   @19889 Created initial version.
@@ -234,6 +234,10 @@
 ** 095 CA  20240324          Performance improvement for runtime annotations: are stored using an integer
 **                           instead of string.
 ** 096 CA  20240423          Always store annotations as their lowercase counterpart.
+** 097 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250423          Added getFilename() back temporarily.
+**     DDF 20250423          Removed getFilename() after removing its usage from TRPL.
+**     DDF 20250424          Renamed getTreeName() to getTreeArtifact().
 */
 
 /*
@@ -299,6 +303,7 @@
 import antlr.*;
 import antlr.collections.*;
 
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.p2j.cfg.Configuration;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.p2j.pattern.*;
@@ -994,43 +999,43 @@
    }
    
    /**
-    * Clear all IDs of an AST and register it under a new filename. The
+    * Clear all IDs of an AST and register it under a new artifact. The
     * AST is walked to fix-up all parent-child relationships as part of
     * the re-initialization.
     * <p>
     * This would be done in order to safely save a copy AST under a
-    * different filename than the source AST. It is necessary in order to
-    * avoid having two ASTs registered to the same filename and ID in the
+    * different artifact than the source AST. It is necessary in order to
+    * avoid having two ASTs registered to the same artifact and ID in the
     * AST registry.
     * <p>
     * No shadow nodes will be created using this version of the method. All
     * hidden tokens will be dropped.
     *
-    * @param   filename
-    *          New filename under which the AST is registered.
+    * @param   artifact
+    *          New artifact under which the AST is registered.
     *
     * @throws  AstException
     *          if any error occurs registering AST or applying fixups.
     */
    @Override
-   public void brainwash(String filename)
+   public void brainwash(Artifact artifact)
    throws AstException
    {
-      brainwash(filename, false);
+      brainwash(artifact, false);
    }
    
    /**
-    * Clear all IDs of an AST and register it under a new filename. The
+    * Clear all IDs of an AST and register it under a new artifact. The
     * AST is walked to fix-up all parent-child relationships as part of
     * the re-initialization.
     * <p>
     * This would be done in order to safely save a copy AST under a
-    * different filename than the source AST. It is necessary in order to
-    * avoid having two ASTs registered to the same filename and ID in the
+    * different artifact than the source AST. It is necessary in order to
+    * avoid having two ASTs registered to the same artifact and ID in the
     * AST registry.
     *
-    * @param   filename
-    *          New filename under which the AST is registered.
+    * @param   artifact
+    *          New artifact under which the AST is registered.
     * @param   shadow
     *          <code>true</code> to create shadow nodes from hidden tokens.
     *          <code>false</code> to drop hidden tokens without creating any
@@ -1039,13 +1044,13 @@
     * @throws  AstException
     *          if any error occurs registering AST or applying fixups.
     */
-   public void brainwash(String filename, boolean shadow)
+   public void brainwash(Artifact artifact, boolean shadow)
    throws AstException
    {
       this.clearIds();
       
       AstManager mgr = AstManager.get();
-      Long       id  = mgr.addTree(filename);
+      Long       id  = mgr.addTree(artifact);
       
       mgr.resetNextNodeId(id);
       this.fixups(null, id, shadow);
@@ -2880,20 +2885,20 @@
    }
    
    /**
-    * Get source code filename associated with the current node. This
+    * Get source code artifact associated with the current node. This
     * information is retrieved from the {@link AstManager} by this AST's
     * ID.
     *
-    * @return  Source code filename associated with this AST or 
+    * @return  Source code artifact associated with this AST or
     *          <code>null</code> if the ID is <code>null</code> or invalid.
     */
-   public String getFilename()
+   public Artifact getArtifact()
    {
       if (id != null)
       {
          try
          {
-            return AstManager.get().getTreeName(id);
+            return AstManager.get().getTreeArtifact(id);
          }
          catch (AstException ae)
          {

=== modified file 'src/com/goldencode/ast/AstManager.java'
--- old/src/com/goldencode/ast/AstManager.java	2024-11-18 11:40:06 +0000
+++ new/src/com/goldencode/ast/AstManager.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : AstManager.java
 ** Abstract : provides services for creating, deleting, editing and using ASTs
 **
-** Copyright (c) 2009-2024, Golden Code Development Corporation.
+** Copyright (c) 2009-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 GES 20090430   @42198 Created initial version. This singleton class provides services for creating,
@@ -17,6 +17,11 @@
 **                           InMemoryRegistryPlugin is not thread-safe.
 **     CA  20211216          initialize(Supplier) returns the AstManager instance.
 ** 007 GES 20241112          Added rewriteTreeName() to fix the data structures for changed filenames.
+** 008 DDF 20250407          Integrated the static block from ScanDriver.
+**     DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250424          Renamed getTreeName() to getTreeArtifact().
+**     DDF 20250430          Removed the static block because import fails due to XmlFilePlugin, which is
+**                           initialized too early.
 */
 
 /*
@@ -76,6 +81,8 @@
 
 import java.util.function.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.p2j.cfg.Configuration;
 import com.goldencode.p2j.security.*;
 
 /**
@@ -111,7 +118,7 @@
  * the same tree.
  * <p>
  * An ID of 0xFFFFFFFFFFFFFFFF is invalid and is the equivalent to a
- * <code>null</code> ID.  Similarly, a tree ID (which by definition has a
+ * <code>null</code> ID.  Similarly, a tree ID (which by definition has
  * a lower DWORD of 0x00000000) can never refer to a specific node and only
  * refers to a tree (to the entire tree rather than any given node of that
  * tree).
@@ -119,7 +126,7 @@
  * This class provides the assignment facility for obtaining unique tree IDs
  * as well as unique node IDs (see {@link #getNextNodeId}).
  * <p>
- * <b>Name lookups are done on a case sensitive basis.</b>
+ * <b>Name lookups are done on a case-sensitive basis.</b>
  * <p>
  * To facilitate multi-threaded applications, a tree level locking facility
  * is provided. {@link #lockTree} and {@link #unlockTree} provide a mechanism
@@ -155,7 +162,7 @@
    
    /** A supplier to create the AST manager plugin, which will be saved in the current context. */
    private final Supplier<AstManagerPlugin> creator;
-   
+
    /**
     * Indicates whether the AST manager, as currently configured, uses memory as its primary
     * storage medium. Delegates to the currently installed plug-in.
@@ -257,7 +264,7 @@
    }
    
    /**
-    * Lookup the name associated with the tree specified by this ID. The ID can
+    * Lookup the artifact associated with the tree specified by this ID. The ID can
     * be a tree ID (an ID that represents an entire AST) or it can be the ID
     * an individual AST node ID. Node IDs encode the tree ID and a portion that
     * is node-specific. The node-specific portion will be ignored.
@@ -266,27 +273,27 @@
     *           The ID to use as a key in the lookup. This ID may be a tree ID
     *           or a specific node ID.
     *
-    * @return   The name associated with the tree or <code>null</code> if no
+    * @return   The artifact associated with the tree or <code>null</code> if no
     *           such tree ID exists.
     */
-   public String getTreeName(long id)
+   public Artifact getTreeArtifact(long id)
    {
-      return getPlugin().getTreeName(id);
+      return getPlugin().getTreeArtifact(id);
    }
    
    /**
-    * Reverse lookup of the ID that corresponds to a given AST name.
-    *
-    * @param    name
-    *           Tree name whose unique ID should be returned.
-    *
-    * @return   The tree ID associated with the name or
-    *           {@link AstManager#INVALID_ID} if the tree name doesn't exist or
+    * Reverse lookup of the ID that corresponds to a given AST artifact.
+    *
+    * @param    artifact
+    *           Tree artifact whose unique ID should be returned.
+    *
+    * @return   The tree ID associated with the artifact or
+    *           {@link AstManager#INVALID_ID} if the tree artifact doesn't exist or
     *           if it is <code>null</code>.
     */
-   public long getTreeId(String name)
+   public long getTreeId(Artifact artifact)
    {
-      return getPlugin().getTreeId(name);
+      return getPlugin().getTreeId(artifact);
    }
    
    /**
@@ -306,63 +313,63 @@
    }
    
    /**
-    * Load a named AST from persistent storage.
+    * Load an artifact AST from persistent storage.
     *
-    * @param    name
-    *           The name of the tree. Often this is a file name.
+    * @param    artifact
+    *           The artifact of the tree.
     *
     * @return   The loaded AST.
     *
     * @throws   AstException
     *           On any error during the loading process.
     * @throws   IllegalArgumentException
-    *           If the name is invalid or the specified resource doesn't exist.
+    *           If the artifact is invalid or the specified resource doesn't exist.
     */
-   public Aast loadTree(String name)
+   public Aast loadTree(Artifact artifact)
    throws AstException,
           IllegalArgumentException
    {
-      return getPlugin().loadTree(name);
+      return getPlugin().loadTree(artifact);
    }
    
    /**
-    * Check if the AST specified by the given name exists already in the plugin's storage.
+    * Check if the AST specified by the given artifact exists already in the plugin's storage.
     * 
-    * @param    name
-    *           The name (including path) of the AST file to be checked.
+    * @param    artifact
+    *           The artifact (including path) of the AST file to be checked.
     * 
     * @return   <code>true</code> if the specified AST is in the plugin's storage.
     */
-   public boolean isExistingAst(String name)
+   public boolean isExistingAst(Artifact artifact)
    {
-      return getPlugin().isExistingAst(name);
+      return getPlugin().isExistingAst(artifact);
    }
    
    /**
-    * Save the given AST to persistent storage under the given name.
+    * Save the given AST to persistent storage under the given artifact.
     *
     * @param    ast
     *           The tree to save.
-    * @param    name
-    *           The name of the tree. Often this is a file name.
+    * @param    artifact
+    *           The artifact of the tree.
     * @param    terse
     *           Do not store line and column data when the node data is saved.
     * 
     * @throws   AstException
     *           On any error during the saving process.
     * @throws   IllegalArgumentException
-    *           If the name is invalid or the specified resource cannot be
+    *           If the artifact is invalid or the specified resource cannot be
     *           used as a target to store the AST.
     */
-   public void saveTree(Aast ast, String name, boolean terse)
+   public void saveTree(Aast ast, Artifact artifact, boolean terse)
    throws AstException,
           IllegalArgumentException
    {
-      getPlugin().saveTree(ast, name, terse);
+      getPlugin().saveTree(ast, artifact, terse);
    }
    
    /**
-    * Obtains a process-wide lock on any processing related to the given tree name. If the lock is already
+    * Obtains a process-wide lock on any processing related to the given tree artifact. If the lock is already
     * held, this method will block until it obtains the lock. When this method returns normally, the calling
     * thread is guaranteed to be holding the lock. This may be called recursively by the same thread and if
     * the current thread is already holding the lock, then this method will only update the reference count
@@ -374,9 +381,9 @@
     * lock. Rather, this lock is a cooperative agreement between all callers (within the same JVM) that no use
     * of the given tree will be made except when the caller holds this exclusive lock to the resource.
     *
-    * @param   name
-    *          Tree name that is associated with the lock. This must be a valid tree name which can be used
-    *          to uniquely identify the tree to be locked.
+    * @param   artifact
+    *          Tree artifact that is associated with the lock. This must be a valid tree artifact which
+    *          can be used to uniquely identify the tree to be locked.
     * @param   orig
     *          {@code true} means the file is part of the original set of files to be converted. Otherwise,
     *          the file is loaded indirectly by pre-scanning a procedure or other class.
@@ -385,18 +392,18 @@
     *          When an interruption occurs during blocking for the lock. If this is thrown, the caller will
     *          NOT be holding the lock.
     * @throws  IllegalArgumentException
-    *          If the name does not represent a valid tree.
+    *          If the artifact does not represent a valid tree.
     */
-   public void lockTree(String name, boolean orig)
+   public void lockTree(Artifact artifact, boolean orig)
    throws InterruptedException,
           IllegalArgumentException
    {
-      long id = getTreeId(name);
+      long id = getTreeId(artifact);
       
       if (id == INVALID_ID)
       {
          // tree doesn't yet exist, create it
-         id = addTree(name);
+         id = addTree(artifact);
          if (orig)
          {
             // mark this as an original file
@@ -457,9 +464,9 @@
     * given tree will be made except when the caller holds this exclusive lock
     * to the resource.
     *
-    * @param    name
-    *           Tree name that is associated with the lock. This must be a
-    *           valid tree name which can be used to uniquely identify the
+    * @param    artifact
+    *           Tree artifact that is associated with the lock. This must be a
+    *           valid tree artifact which can be used to uniquely identify the
     *           tree to be locked.
     *
     * @throws   IllegalMonitorStateException
@@ -467,11 +474,11 @@
     * @throws   IllegalArgumentException
     *           If the name does not represent a valid tree.
     */
-   public void unlockTree(String name)
+   public void unlockTree(Artifact artifact)
    throws IllegalMonitorStateException,
           IllegalArgumentException
    {
-      unlockTree(getTreeId(name));
+      unlockTree(getTreeId(artifact));
    }
    
    /**
@@ -506,16 +513,16 @@
    }
 
    /**
-    * Fixup the data structures to reference a different name.
+    * Fixup the data structures to reference a different artifact.
     * 
-    * @param    oldname
-    *           File name currently in the data structures.
-    * @param    newname
-    *           Name to be inserted into the data structures.
+    * @param    oldArtifact
+    *           Artifact currently in the data structures.
+    * @param    newArtifact
+    *           Artifact to be inserted into the data structures.
     */
-   public void rewriteTreeName(String oldname, String newname)
+   public void rewriteTreeArtifact(Artifact oldArtifact, Artifact newArtifact)
    {
-      getPlugin().rewriteTreeName(oldname, newname);
+      getPlugin().rewriteTreeArtifact(oldArtifact, newArtifact);
    }
       
    /**
@@ -564,19 +571,19 @@
     * tree ID is assigned and this mapping is added to the registry. If the
     * tree name already exists, the existing tree ID is returned.
     *
-    * @param    name
-    *           The tree name. Often this will be the name of a file.
+    * @param    artifact
+    *           The tree artifact.
     *
     * @return   The assigned tree ID.
     *
     * @throws   AstException
     *           If too many ASTs exist in the registry or if the input
-    *           name is <code>null</code>.
+    *           artifact is <code>null</code>.
     */
-   public long addTree(String name)
+   public long addTree(Artifact artifact)
    throws AstException
    {
-      return getPlugin().addTree(name);
+      return getPlugin().addTree(artifact);
    }
    
    /**
@@ -597,16 +604,16 @@
    /**
     * Removes all mapping data referencing the specified tree.
     *
-    * @param    name
-    *           The tree name to remove.
+    * @param    artifact
+    *           The tree artifact to remove.
     *
     * @return   <code>true</code> if the tree existed in the registry (and 
     *           was removed), <code>false</code> if the tree didn't exist.
     */
-   public boolean removeTree(String name)
+   public boolean removeTree(Artifact artifact)
    throws AstException
    {
-      return getPlugin().removeTree(getTreeId(name));
+      return getPlugin().removeTree(getTreeId(artifact));
    }
    
    /**
@@ -648,7 +655,7 @@
    }
 
    /**
-    * Get the AST manager plugin for this current context.  If it is not yet set, it will be assigned to a 
+    * Get the AST manager plugin for this current context.  If it is not yet set, it will be assigned to an
     * instance returned by the {@link #creator supplier}.
     * 
     * @return    See above.

=== modified file 'src/com/goldencode/ast/AstManagerPlugin.java'
--- old/src/com/goldencode/ast/AstManagerPlugin.java	2024-11-18 11:40:06 +0000
+++ new/src/com/goldencode/ast/AstManagerPlugin.java	2025-05-09 07:59:16 +0000
@@ -3,7 +3,7 @@
 ** Abstract : defines the API for an AstManager plugin that implements services
 **            for creating, deleting, editing and using ASTs
 **
-** Copyright (c) 2009-2024, Golden Code Development Corporation.
+** Copyright (c) 2009-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 GES 20090430   @42199 Created initial version. This defines the API for an AstManager plugin that
@@ -16,6 +16,8 @@
 **     IAS 20211028          Added conversion of a DOM node to a formatted string (for debugging).
 **     ECF 20211108          Format cleanup.
 ** 007 GES 20241112          Added rewriteTreeName() to fix the data structures for changed filenames.
+** 008 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250424          Renamed getTreeName() to getTreeArtifact().
 */
 
 /*
@@ -77,7 +79,10 @@
 import javax.xml.transform.*;
 import javax.xml.transform.dom.*;
 import javax.xml.transform.stream.*;
+
+import com.goldencode.artifacts.Artifact;
 import org.w3c.dom.*;
+
 /**
  * This defines the API for an {@link AstManager} plugin that implements
  * services for creating, deleting, editing and using Abstract Syntax Trees
@@ -138,7 +143,7 @@
    public boolean usesMemoryStorage();
    
    /**
-    * Lookup the name associated with the tree specified by this ID. The ID can
+    * Lookup the artifact associated with the tree specified by this ID. The ID can
     * be a tree ID (an ID that represents an entire AST) or it can be the ID
     * an individual AST node ID. Node IDs encode the tree ID and a portion that
     * is node-specific. The node-specific portion will be ignored.
@@ -147,20 +152,20 @@
     *           The ID to use as a key in the lookup. This ID may be a tree ID
     *           or a specific node ID.
     *
-    * @return   The name associated with the tree or {@code null} if no such tree ID exists.
+    * @return   The artifact associated with the tree or {@code null} if no such tree ID exists.
     */
-   public String getTreeName(long id);
+   public Artifact getTreeArtifact(long id);
    
    /**
-    * Reverse lookup of the ID that corresponds to a given AST name.
-    *
-    * @param   name
-    *          Tree name whose unique ID should be returned.
-    *
-    * @return  The tree ID associated with the name or {@link AstManager#INVALID_ID} if the tree name doesn't
-    *          exist or if it is {@code null}.
+    * Reverse lookup of the ID that corresponds to a given AST artifact.
+    *
+    * @param   artifact
+    *          Tree artifact whose unique ID should be returned.
+    *
+    * @return  The tree ID associated with the artifact or {@link AstManager#INVALID_ID} if the tree
+    *          artifact doesn't exist or if it is {@code null}.
     */
-   public long getTreeId(String name);
+   public long getTreeId(Artifact artifact);
    
    /**
     * Returns the tree ID portion of a node ID.
@@ -174,10 +179,10 @@
    public long getTreeId(long id);
    
    /**
-    * Load a named AST from persistent storage.
+    * Load an artifact AST from persistent storage.
     *
-    * @param    name
-    *           The name of the tree. Often this is a file name.
+    * @param    artifact
+    *           The artifact of the tree.
     *
     * @return   The loaded AST.
     *
@@ -186,17 +191,17 @@
     * @throws   IllegalArgumentException
     *           If the name is invalid or the specified resource doesn't exist.
     */
-   public Aast loadTree(String name)
+   public Aast loadTree(Artifact artifact)
    throws AstException,
           IllegalArgumentException;
    
    /**
-    * Save the given AST to persistent storage under the given name.
+    * Save the given AST to persistent storage under the given artifact.
     *
     * @param    ast
     *           The tree to save.
-    * @param    name
-    *           The name of the tree. Often this is a file name.
+    * @param    artifact
+    *           The artifact of the tree. Often this is a file artifact.
     * @param    terse
     *           Do not store line and column data when the node data is saved.
     * 
@@ -205,7 +210,7 @@
     * @throws   IllegalArgumentException
     *           If the name is invalid or the specified resource cannot be used as a target to store the AST.
     */
-   public void saveTree(Aast ast, String name, boolean terse)
+   public void saveTree(Aast ast, Artifact artifact, boolean terse)
    throws AstException,
           IllegalArgumentException;
    
@@ -308,15 +313,15 @@
     * tree ID is assigned and this mapping is added to the registry. If the
     * tree name already exists, the existing tree ID is returned.
     *
-    * @param    name
-    *           The tree name. Often this will be the name of a file.
+    * @param    artifact
+    *           The tree artifact.
     *
     * @return   The assigned tree ID.
     *
     * @throws   AstException
-    *           If too many ASTs exist in the registry or if the input name is {@code null}.
+    *           If too many ASTs exist in the registry or if the input artifact is {@code null}.
     */
-   public long addTree(String name)
+   public long addTree(Artifact artifact)
    throws AstException;
    
    /**
@@ -341,24 +346,24 @@
    throws AstException;
    
    /**
-    * Check if the AST specified by the given name exists already in the plugin's storage.
+    * Check if the AST specified by the given artifact exists already in the plugin's storage.
     * 
-    * @param   name
-    *          The name (including path) of the AST file to be checked.
+    * @param   artifact
+    *          The artifact (including path) of the AST file to be checked.
     * 
     * @return  {@code true} if the specified AST is in the plugin's storage.
     */
-   public boolean isExistingAst(String name);
+   public boolean isExistingAst(Artifact artifact);
    
    /**
-    * Fixup the data structures to reference a different name.
+    * Fixup the data structures to reference a different artifact.
     * 
-    * @param    oldname
-    *           File name currently in the data structures.
-    * @param    newname
-    *           Name to be inserted into the data structures.
+    * @param    oldArtifact
+    *           Artifact currently in the data structures.
+    * @param    newArtifact
+    *           Artifact to be inserted into the data structures.
     */
-   public void rewriteTreeName(String oldname, String newname);
+   public void rewriteTreeArtifact(Artifact oldArtifact, Artifact newArtifact);
    
    /**
     * Sets a flag (or a {@code or} combination of them) for a specific tree.

=== modified file 'src/com/goldencode/ast/BasePlugin.java'
--- old/src/com/goldencode/ast/BasePlugin.java	2024-11-18 11:40:06 +0000
+++ new/src/com/goldencode/ast/BasePlugin.java	2025-05-09 07:59:16 +0000
@@ -3,7 +3,7 @@
 ** Abstract : maintains a project-specific master bidirectional indexing
 **            facility for tree names to unique IDs
 **
-** Copyright (c) 2005-2024, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 GES 20090513   @42200 Refactored code from AstRegistry to implement the first version.
@@ -11,6 +11,8 @@
 ** 003 OM  20210913          Added optional flags capability to stored AST nodes and API to query a list of
 **                           ASTs based on their flags.
 ** 004 GES 20241112          Added rewriteTreeName() to fix the data structures for changed filenames.
+** 005 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250424          Renamed getTreeName() to getTreeArtifact().
 */
 
 /*
@@ -68,6 +70,8 @@
 
 package com.goldencode.ast;
 
+import com.goldencode.artifacts.Artifact;
+
 import java.util.*;
 import java.util.concurrent.locks.*;
 
@@ -87,10 +91,10 @@
    /** Specifies the maximum possible number of files in the registry. */
    protected static final long MAX_POSSIBLE = Integer.MAX_VALUE;
    
-   /** Stores the tree name to ID mapping information. */
-   protected Map<String, Long> fnames = new HashMap<>();
+   /** Stores the tree artifact to ID mapping information. */
+   protected Map<Artifact, Long> fnames = new HashMap<>();
       
-   /** Stores the ID to tree name mapping information. */
+   /** Stores the ID to tree artifact mapping information. */
    protected Map<Long, Pair> fids = new HashMap<>();
    
    /** Stores the tree name to lock mapping information. */
@@ -111,7 +115,7 @@
    }
    
    /**
-    * Lookup the name associated with the tree specified by this ID. The ID can
+    * Lookup the artifact associated with the tree specified by this ID. The ID can
     * be a tree ID (an ID that represents an entire AST) or it can be the ID
     * an individual AST node ID. Node IDs encode the tree ID and a portion that
     * is node-specific. The node-specific portion will be ignored.
@@ -120,13 +124,13 @@
     *           The ID to use as a key in the lookup. This ID may be a tree ID
     *           or a specific node ID.
     *
-    * @return   The name associated with the tree or <code>null</code> if no
+    * @return   The artifact associated with the tree or <code>null</code> if no
     *           such tree ID exists.
     */
-   public String getTreeName(long id)
+   public Artifact getTreeArtifact(long id)
    {
       Pair   pair = null;
-      String name = null;
+      Artifact artifact = null;
       
       if (id != AstManager.INVALID_ID)
       {
@@ -136,61 +140,61 @@
             
             if (pair != null)
             {
-               name = pair.name;
+               artifact = pair.artifact;
             }
          }
       }
       
-      return name;
+      return artifact;
    }
    
    /**
-    * Fixup the data structures to reference a different name.
+    * Fixup the data structures to reference a different artifact.
     * 
-    * @param    oldname
-    *           File name currently in the data structures.
-    * @param    newname
-    *           Name to be inserted into the data structures.
+    * @param    oldArtifact
+    *           Artifact currently in the data structures.
+    * @param    newArtifact
+    *           Artifact to be inserted into the data structures.
     */
-   public void rewriteTreeName(String oldname, String newname)
+   public void rewriteTreeArtifact(Artifact oldArtifact, Artifact newArtifact)
    {
       synchronized (this)
       {
-         Long id = fnames.remove(oldname);
+         Long id = fnames.remove(oldArtifact);
          
          if (id != AstManager.INVALID_ID)
          {
-            fnames.put(newname, id);
+            fnames.put(newArtifact, id);
                
             Pair pair = fids.get(id);
                
             if (pair != null)
             {
-               pair.name = newname;
+               pair.artifact = newArtifact;
             }
          }            
       }
    }
    
    /**
-    * Reverse lookup of the ID that corresponds to a given AST name.
-    *
-    * @param    name
-    *           Tree name whose unique ID should be returned.
-    *
-    * @return   The tree ID associated with the name or
-    *           {@link AstManager#INVALID_ID} if the tree name doesn't exist or
+    * Reverse lookup of the ID that corresponds to a given AST artifact.
+    *
+    * @param    artifact
+    *           Tree artifact whose unique ID should be returned.
+    *
+    * @return   The tree ID associated with the artifact or
+    *           {@link AstManager#INVALID_ID} if the tree artifact doesn't exist or
     *           is <code>null</code>.
     */
-   public long getTreeId(String name)
+   public long getTreeId(Artifact artifact)
    {
       long id = AstManager.INVALID_ID;
       
-      if (name != null)
+      if (artifact != null)
       {
          synchronized (this)
          {
-            Long possible = fnames.get(name);
+            Long possible = fnames.get(artifact);
             
             if (possible != null)
             {
@@ -394,26 +398,26 @@
    /**
     * Adds a mapping from the specified AST to a new ID. The next available
     * tree ID is assigned and this mapping is added to the registry. If the
-    * tree name already exists, the existing tree ID is returned.
+    * tree artifact already exists, the existing tree ID is returned.
     *
-    * @param    name
-    *           The tree name. Often this will be the name of a file.
+    * @param    artifact
+    *           The tree artifact.
     *
     * @return   The assigned tree ID.
     *
     * @throws   AstException
     *           If too many ASTs exist in the registry or if the input
-    *           name is <code>null</code>.
+    *           artifact is <code>null</code>.
     */
-   public long addTree(String name)
+   public long addTree(Artifact artifact)
    throws AstException
    {
-      if (name == null)
+      if (artifact == null)
       {
-         throw new AstException("Cannot add null source name to the registry.");
+         throw new AstException("Cannot add null source artifact to the registry.");
       }
       
-      long result = getTreeId(name);
+      long result = getTreeId(artifact);
       
       if (result == AstManager.INVALID_ID)
       {
@@ -440,13 +444,13 @@
                // node IDs to get the next possible file ID
                result = possible << HIGH_BITS;
                
-            } while (getTreeName(result) != null);
+            } while (getTreeArtifact(result) != null);
             
             // the first node ID is stored in the next member of the inner class Pair, this value is set to
             // the file ID + 1 which is the first unique AST node ID
-            Pair newFile = new Pair(name, result + 1);
+            Pair newFile = new Pair(artifact, result + 1);
             fids.put(result, newFile);
-            fnames.put(name, result);
+            fnames.put(artifact, result);
             
             // mark the registry as needing a save
             dirty = true;
@@ -484,7 +488,7 @@
             return false;
          }
          
-         fnames.remove(pair.name);
+         fnames.remove(pair.artifact);
          fids.remove(id);
          
          // mark the registry as needing a save
@@ -610,7 +614,7 @@
          // check if all flags are set
          if ((pair.flags & flags) == flags)
          {
-            gather[count++] = pair.name;
+            gather[count++] = pair.artifact.getRelativePath();
          }
       }
       
@@ -628,13 +632,13 @@
    }
    
    /**
-    * Simple container class to store the next available ID for a tree, the associated tree name and whether
-    * it was part of the original set of source files.
+    * Simple container class to store the next available ID for a tree, the associated tree artifact and
+    * whether it was part of the original set of source files.
     */
    protected static class Pair
    {
-      /** A tree name. */
-      protected String name;
+      /** A tree artifact. */
+      protected Artifact artifact;
       
       /**
        * Was this file marked in any way? <br>
@@ -651,14 +655,14 @@
       /**
        * Default constructor.
        *
-       * @param    name
-       *           A tree name.
+       * @param    artifact
+       *           A tree artifact.
        * @param    next
        *           The next available ID associated with this tree.
        */ 
-      protected Pair(String name, long next)
+      protected Pair(Artifact artifact, long next)
       {
-         this.name = name;
+         this.artifact = artifact;
          this.next = next;
       }
    }

=== modified file 'src/com/goldencode/ast/InMemoryRegistryPlugin.java'
--- old/src/com/goldencode/ast/InMemoryRegistryPlugin.java	2020-04-27 20:45:31 +0000
+++ new/src/com/goldencode/ast/InMemoryRegistryPlugin.java	2025-05-09 07:59:16 +0000
@@ -2,13 +2,16 @@
 ** Module   : InMemoryRegistryPlugin.java
 ** Abstract : implements an in-memory AST storage option for an AstManagerPlugin
 **
-** Copyright (c) 2013-2017, Golden Code Development Corporation.
+** Copyright (c) 2013-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description----------------------------------
 ** 001 CA  20131023 Created first version.
 ** 002 ECF 20140919 Fixed memory leaks, added usesMemoryStorage method, to help with memory
 **                  management.
+** 003 DDF 20250414 Make use of artifacts instead of the filename.
+**     DDF 20250424 Renamed getTreeName() to getTreeArtifact().
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -64,6 +67,8 @@
 
 package com.goldencode.ast;
 
+import com.goldencode.artifacts.Artifact;
+
 import java.util.*;
 
 /**
@@ -72,8 +77,8 @@
 public class InMemoryRegistryPlugin
 extends BasePlugin
 {
-   /** In-memory storage of ASTs, by their name. */
-   private final Map<String, Aast> asts = new HashMap<String, Aast>();
+   /** In-memory storage of ASTs, by their artifact. */
+   private final Map<Artifact, Aast> asts = new HashMap<Artifact, Aast>();
    
    /**
     * Does this plug-in use memory as its primary storage medium? This implementation overrides
@@ -87,25 +92,25 @@
    }
    
    /**
-    * Load a named AST from the in-memory storage, by returning a copy of the {@link #asts}
+    * Load an artifact AST from the in-memory storage, by returning a copy of the {@link #asts}
     * entry after using the specified <code>name</code> as the key.
     *
-    * @param    name
-    *           The name of the tree. Often this is a file name.
+    * @param    artifact
+    *           The artifact of the tree.
     *
     * @return   The loaded AST.
     *
     * @throws   AstException
     *           On any error during the loading process.
     * @throws   IllegalArgumentException
-    *           If the name is invalid or the specified resource doesn't exist.
+    *           If the artifact is invalid or the specified resource doesn't exist.
     */
    @Override
-   public Aast loadTree(String name)
+   public Aast loadTree(Artifact artifact)
    throws AstException,
           IllegalArgumentException
    {
-      Aast ast = asts.get(name);
+      Aast ast = asts.get(artifact);
       
       Aast clone = ast.duplicate();
       clone.removeAnnotation("terse");
@@ -119,28 +124,28 @@
    }
 
    /**
-    * Save the given AST to in-memory storage under the given name. This will save a copy of the
-    * specified AST in the {@link #asts} map, usin the specified name as the key.
+    * Save the given AST to in-memory storage under the given artifact. This will save a copy of the
+    * specified AST in the {@link #asts} map, using the specified artifact as the key.
     *
     * @param    ast
     *           The tree to save.
-    * @param    name
-    *           The name of the tree. Often this is a file name.
+    * @param    artifact
+    *           The artifact of the tree.
     * @param    terse
     *           Do not store line and column data when the node data is saved.
     * 
     * @throws   AstException
     *           On any error during the saving process.
     * @throws   IllegalArgumentException
-    *           If the name is invalid or the specified resource cannot be
+    *           If the artifact is invalid or the specified resource cannot be
     *           used as a target to store the AST.
     */
    @Override
-   public void saveTree(Aast ast, String name, boolean terse)
+   public void saveTree(Aast ast, Artifact artifact, boolean terse)
    throws AstException,
           IllegalArgumentException
    {
-      addTree(name);
+      addTree(artifact);
       
       Aast clone = ast.duplicate();
       
@@ -150,7 +155,7 @@
          terseTree(clone);
       }
       
-      asts.put(name, clone);
+      asts.put(artifact, clone);
    }
 
    /**
@@ -178,23 +183,23 @@
    public boolean removeTree(long id)
    throws AstException
    {
-      String name = getTreeName(id);
-      asts.remove(name);
+      Artifact artifact = getTreeArtifact(id);
+      asts.remove(artifact);
 
       return super.removeTree(id);
    }
 
    /**
-    * Check if the AST specified by the given name exists already in the plugin's storage.
+    * Check if the AST specified by the given artifact exists already in the plugin's storage.
     * 
-    * @param    name
-    *           The name (including path) of the AST file to be checked.
+    * @param    artifact
+    *           The artifact of the AST file to be checked.
     * 
     * @return   <code>true</code> if the specified AST is in the plugin's storage.
     */
-   public boolean isExistingAst(String name)
+   public boolean isExistingAst(Artifact artifact)
    {
-      return asts.containsKey(name);
+      return asts.containsKey(artifact);
    }
    
    /**

=== modified file 'src/com/goldencode/ast/XmlFilePlugin.java'
--- old/src/com/goldencode/ast/XmlFilePlugin.java	2024-07-05 10:05:22 +0000
+++ new/src/com/goldencode/ast/XmlFilePlugin.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : XmlFilePlugin.java
 ** Abstract : implements an XML file storage option for an AstManagerPlugin
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 GES 20090513   @42201 Refactored code from AstRegistry and AstPersister to implement the first version.
@@ -28,6 +28,8 @@
 **     TJD 20220504          Java 11 compatibility minor changes.
 ** 014 OM  20230915          Allowed to load resources from FS but log it as error.
 ** 015 ICP 20240705          Fixed method of obtaining the class loader.
+** 016 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250430          Use the resource path when looking for a resource using the class loader.
 */
 
 /*
@@ -89,7 +91,10 @@
 import java.util.*;
 import java.util.concurrent.*;
 import javax.xml.parsers.*;
+
 import org.w3c.dom.*;
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.util.*;
 import com.goldencode.p2j.classloader.MultiClassLoader;
 import antlr.*;
@@ -272,26 +277,26 @@
    }
    
    /**
-    * Reverse lookup of the ID that corresponds to a given AST name.
-    *
-    * @param    name
-    *           Tree name whose unique ID should be returned.
-    *
-    * @return   The tree ID associated with the name or
-    *           {@link AstManager#INVALID_ID} if the tree name doesn't exist or
+    * Reverse lookup of the ID that corresponds to a given AST artifact.
+    *
+    * @param    artifact
+    *           Tree artifact whose unique ID should be returned.
+    *
+    * @return   The tree ID associated with the artifact or
+    *           {@link AstManager#INVALID_ID} if the tree artifact doesn't exist or
     *           is <code>null</code>.
     */
-   public long getTreeId(String name)
+   public long getTreeId(Artifact artifact)
    {
-      long id = super.getTreeId(name);
+      long id = super.getTreeId(artifact);
       
       if (id == AstManager.INVALID_ID)
       {
          // try removing a well known suffix
-         if (name.endsWith(AST_POSTFIX))
+         if (artifact.endsWith(AST_POSTFIX))
          {
-            int len = name.length() - AST_POSTFIX_LEN;
-            id = super.getTreeId(name.substring(0, len));
+            Artifact originalArtifact = ArtifactManager.getOriginalArtifact(artifact, AST_POSTFIX);
+            id = super.getTreeId(originalArtifact);
          }
       }
       
@@ -411,19 +416,19 @@
    }
    
    /**
-    * Load a named AST from persistent storage.
+    * Load an artifact AST from persistent storage.
     *
-    * @param    name
-    *           The name of the tree. Often this is a file or resource name.
+    * @param    artifact
+    *           The artifact of the tree.
     *
     * @return   The loaded AST.
     *
     * @throws   AstException
     *           On any error during the loading process.
     * @throws   IllegalArgumentException
-    *           If the name is invalid or the specified resource doesn't exist.
+    *           If the artifact is invalid or the specified resource doesn't exist.
     */
-   public Aast loadTree(String name)
+   public Aast loadTree(Artifact artifact)
    throws AstException,
           IllegalArgumentException
    {
@@ -434,57 +439,58 @@
       {
          if (useResources) // more like 'prefer resources first'
          {
-            InputStream stream = MultiClassLoader.getClassLoader().getResourceAsStream(name);
+            InputStream stream = MultiClassLoader.getClassLoader()
+                                                 .getResourceAsStream(artifact.getResourcePath());
             if (stream != null)
             {
                dom = XmlHelper.parse(stream);
             }
          }
          
-         // NOTE: if !useResources and [name] failed to be load as a resource do an attempt to load it from
+         // NOTE: if !useResources and [artifact] failed to be load as a resource do an attempt to load it from
          //       file system. This should only happen when in developer mode. In production this should be
          //       a configuration error (or lack or resource itself).
          if (dom == null)
          {
-            File file = new File(name);
-            if (file.exists() && file.isFile())
+            if (artifact.exists())
             {
-               dom  = XmlHelper.parse(file);
+               dom  = XmlHelper.parse(artifact.getFile());
             }
          }
          
          if (dom == null)
          {
-            throw new IllegalArgumentException("Missing/bogus input file/resource " + name);
+            throw new IllegalArgumentException("Missing/bogus input file/resource " +
+                                               artifact.getRelativePath());
          }
          
          root = dom.getDocumentElement();
       }
       catch (Exception excpt)
       {
-         throw new AstException("Error loading AST XML file/resource: " + name, excpt);
+         throw new AstException("Error loading AST XML file/resource: " + artifact.getRelativePath(), excpt);
       }
       
       return loadTree(root, false);
    }
    
    /**
-    * Save the given AST to persistent storage under the given name.
+    * Save the given AST to persistent storage under the given artifact.
     *
     * @param    ast
     *           The tree to save.
-    * @param    name
-    *           The name of the tree. Often this is a file name.
+    * @param    artifact
+    *           The artifact of the tree.
     * @param    terse
     *           Do not store line and column data when the node data is saved.
     * 
     * @throws   AstException
     *           On any error during the saving process.
     * @throws   IllegalArgumentException
-    *           If the name is invalid or the specified resource cannot be
+    *           If the artifact is invalid or the specified resource cannot be
     *           used as a target to store the AST.
     */
-   public void saveTree(Aast ast, String name, boolean terse)
+   public void saveTree(Aast ast, Artifact artifact, boolean terse)
    throws AstException,
           IllegalArgumentException
    {
@@ -493,7 +499,7 @@
          throw new IllegalArgumentException("Cannot persist null AST.");
       }
       
-      File file = new File(name);
+      File file = artifact.getFile();
       
       // create path if necessary
       File dir = file.getParentFile();
@@ -538,20 +544,20 @@
       writeTreeLocal(ast, dom, elem, terse);
       
       // persist the XML document as a file
-      persist(dom, new File(name));
+      persist(dom, file);
    }
    
    /**
-    * Check if the AST specified by the given name exists already in the plugin's storage.
+    * Check if the AST specified by the given artifact exists already in the plugin's storage.
     * 
-    * @param    name
-    *           The name (including path) of the AST file to be checked.
+    * @param    artifact
+    *           The artifact (including path) of the AST file to be checked.
     * 
     * @return   <code>true</code> if the specified AST is in the plugin's storage.
     */
-   public boolean isExistingAst(String name)
+   public boolean isExistingAst(Artifact artifact)
    {
-      return new File(name).exists();
+      return artifact.getFile().exists();
    }
    
    /**
@@ -602,7 +608,7 @@
          
          Element map = dom.createElement(ELEM_MAP);
          map.setAttribute(ATTR_ID, current.toString());
-         map.setAttribute(ATTR_TREE, pair.name);
+         map.setAttribute(ATTR_TREE, pair.artifact.getRelativePath());
          map.setAttribute(ATTR_NEXT, Long.toString(pair.next));
          if (pair.flags != 0)
          {
@@ -661,14 +667,15 @@
          Long   id    = Long.valueOf(elem.getAttribute(ATTR_ID));
          Long   next  = Long.valueOf(elem.getAttribute(ATTR_NEXT));
          String fname = elem.getAttribute(ATTR_TREE);
+         Artifact artifact = ArtifactManager.addArtifact(fname);
          String flags = elem.getAttribute(ATTR_FLAGS);
-         Pair   pair  = new Pair(fname, next);
+         Pair   pair  = new Pair(artifact, next);
          if (!flags.isEmpty())
          {
             pair.flags = Long.parseLong(flags);
          }
          
-         fnames.put(fname, id);
+         fnames.put(artifact, id);
          fids.put(id, pair);
       }
    }

=== modified file 'src/com/goldencode/p2j/cfg/Configuration.java'
--- old/src/com/goldencode/p2j/cfg/Configuration.java	2024-07-09 15:28:19 +0000
+++ new/src/com/goldencode/p2j/cfg/Configuration.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : Configuration.java
 ** Abstract : Master configuration object
 **
-** Copyright (c) 2004-2024, Golden Code Development Corporation.
+** Copyright (c) 2004-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
 ** 001 ECF 20041217   @19157 Created initial version.
@@ -87,6 +87,10 @@
 ** 025 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
 ** 026 ICP 20240705          Fixed method of obtaining the class loader.
 ** 027 GBB 20240709          Making PROP_HOME_DIR public to be reused.
+** 028 GES 20230524          Moved some code to a more generic package.
+**     DDF 20250320          Added absoluteFilename() method.
+**     DDF 20250327          Replaced FileList usage with ArtifactCollection.
+**     DDF 20250423          Make us of artifacts instead of filenames.
 */
 
 /*
@@ -154,11 +158,12 @@
 import com.goldencode.p2j.classloader.MultiClassLoader;
 import com.goldencode.p2j.util.logging.*;
 import org.xml.sax.*;
+
+import com.goldencode.artifacts.*;
 import com.goldencode.p2j.schema.*;
 import com.goldencode.p2j.schema.SchemaConfig.*;
 import com.goldencode.p2j.schema.SchemaConfig.NsConfig.*;
 import com.goldencode.p2j.uast.*;
-import com.goldencode.p2j.util.*;
 import com.goldencode.util.*;
 
 /**
@@ -350,8 +355,8 @@
     */
    private ProfileConfig activeProfile = null;
    
-   /** The resolved files from the {@link #fileSet}, if available. */
-   private FileList fileList;
+   /** The resolved artifacts from the {@link #fileSet}, if available. */
+   private ArtifactCollection artifactCol;
 
    /** The loaded schema configuration. */
    private SchemaConfig schemaConfig = null;
@@ -540,9 +545,9 @@
    /**
     * Resolve the configured {@link #fileSet}.
     * 
-    * @return   The files used for conversion.
+    * @return   The artifacts used for conversion.
     */
-   public static FileList getFileList()
+   public static ArtifactCollection getArtifactCol()
    {
       Configuration cfg = getInstance();
       
@@ -570,13 +575,12 @@
          return null;
       }
       
-      if (cfg.fileList == null)
+      if (cfg.artifactCol == null)
       {
          try
          {
             boolean caseSens = isCaseSensitive();
-            ExplicitFileList res = new ExplicitFileList(caseSens);
-
+            List<ArtifactCollection> cols = new ArrayList<>();
             for (Map.Entry<String, FileSet> entry : cfg.fileSet.entrySet())
             {
                String profile = entry.getKey();
@@ -588,16 +592,16 @@
                   continue;
                }
                
-               FileList fl = processFileSet(fileSet, caseSens);
-               if (fl == null)
+               ArtifactCollection col = ArtifactManager.processFileSet(fileSet, caseSens);
+               if (col.isEmpty())
                {
                   return null;
                }
-               
-               res.addAll(fl.listFilenames());
+
+               cols.add(col);
             }
             
-            cfg.fileList = res;
+            cfg.artifactCol = ArtifactManager.mergeArtifactCollections(cols);
          }
          catch (IOException e)
          {
@@ -606,7 +610,7 @@
          }
       }
       
-      return cfg.fileList;
+      return cfg.artifactCol;
    }
    
    /**
@@ -850,23 +854,16 @@
                continue;
             }
             
-            Set<String> fl = cfg.getFileList();
-            if (fl == null)
+            ArtifactCollection col = cfg.getArtifactCol();
+            if (col == null)
             {
                try
                {
-                  FileList fileList = processFileSet(cfg.getFileSet(), importMode);
-                  if (fileList == null)
+                  ArtifactCollection fileList = processFileSet(cfg.getFileSet(), importMode);
+                  if (fileList == null || fileList.isEmpty())
                   {
                      continue;
                   }
-                  
-                  fl = new HashSet<>();
-                  for (String fn : fileList.listFilenames())
-                  {
-                     fn = normalizeFilename(fn);
-                     fl.add(fn);
-                  }
                }
                catch (IOException e)
                {
@@ -875,10 +872,10 @@
                   continue;
                }
                
-               cfg.setFileList(fl);
+               cfg.setArtifactCol(col);
             }
             
-            if (fl.contains(file))
+            if (ArtifactManager.containsArtifact(col, file))
             {
                profile = p;
             }
@@ -997,9 +994,8 @@
                continue;
             }
             
-            String dictFile = schemaConfig.getSchemaFileName(profile, ns, ".dict");
-            dictFile = normalizeFilename(dictFile);
-            fileProfile.put(dictFile, profile);
+            Artifact dictArtifact = schemaConfig.getSchemaFileName(profile, ns, ".dict");
+            fileProfile.put(dictArtifact.getRelativePath(), profile);
             dbProfile.computeIfAbsent(ns.toLowerCase(), (k) -> new HashSet<>()).add(profile);
          }
       }
@@ -1133,7 +1129,7 @@
          this.fileSet = new HashMap<>();
          this.fileSet.put(profile.getName(), profile.getFileSet());
       }
-      this.fileList = null;
+      this.artifactCol = null;
       
       // clear the cvtPath
       this.cvtpath = null;
@@ -1328,7 +1324,31 @@
       // project directory tree, so we can only return the original filename
       return filename;
    }
-   
+
+   /**
+    * Converts a relative filename (relative to the current
+    * directory) into a absolute path for the filename.
+    *
+    * @param    filename
+    *           The filename.
+    *
+    * @return   The absolute path of the filename.
+    */
+   public static String absoluteFilename(String filename)
+   {
+      File abnormal;
+      try
+      {
+         abnormal = toFile(filename);
+      }
+      catch (ConfigurationException cfg)
+      {
+         return null;
+      }
+
+      return abnormal.getAbsolutePath();
+   }
+
    /**
     * Get the singleton instance of this class.
     *
@@ -1639,12 +1659,12 @@
     * @param    caseSens
     *           Flag indicating if the legacy operating system is case-sensitive or not.
     *           
-    * @return   The resolved file list.
+    * @return   The resolved artifact collection.
     * 
     * @throws   IOException
     *           In case of problems reading files from disk.
     */
-   private static FileList processFileSet(FileSet fileSet, boolean caseSens) 
+   private static ArtifactCollection processFileSet(FileSet fileSet, boolean caseSens)
    throws IOException
    {
       if (fileSet.getFilename() != null)
@@ -1664,9 +1684,9 @@
             return null;
          }
       }
-      
-      FileList fl = FileListFactory.processFileSet(fileSet, caseSens);
-      return fl;
+
+      ArtifactCollection col = ArtifactManager.processFileSet(fileSet, caseSens);
+      return col;
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/cfg/ProfileConfig.java'
--- old/src/com/goldencode/p2j/cfg/ProfileConfig.java	2022-05-01 17:28:59 +0000
+++ new/src/com/goldencode/p2j/cfg/ProfileConfig.java	2025-05-09 07:59:16 +0000
@@ -2,11 +2,14 @@
 ** Module   : ProfileConfig.java
 ** Abstract : A 'profile' configuration read from the main configuration file.
 **
-** Copyright (c) 2022, Golden Code Development Corporation.
+** Copyright (c) 2022-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------- Description --------------------------------------
 ** 001 CA  20220430 Created initial version.
+** 002 GES 20230524 Moved some code to a more generic package.
+**     DDF 20250327 Replaced FileList usage with ArtifactCollection.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -64,6 +67,7 @@
 
 import java.util.*;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.p2j.schema.*;
 
 /**
@@ -98,8 +102,8 @@
    /** The PROPATH paths for this config. */
    private String[] paths;
 
-   /** The file list resolved from the {@link #fileSet}. */
-   private Set<String> fileList;
+   /** The artifact collection resolved from the {@link #fileSet}. */
+   private ArtifactCollection artifactCol;
    
    /**
     * Create a new instance.
@@ -323,23 +327,23 @@
    }
 
    /**
-    * Get the profile's file-list.
+    * Get the profile's artifact collection.
     * 
-    * @return   The {@link #fileList}.
+    * @return   The {@link #artifactCol}.
     */
-   Set<String> getFileList()
+   ArtifactCollection getArtifactCol()
    {
-      return fileList;
+      return artifactCol;
    }
 
    /**
-    * Set the profile's file-list.
+    * Set the profile's artifact collection.
     * 
-    * @param    fl
-    *           The file-list associated with the {@link #fileSet}.
+    * @param    col
+    *           The artifact collection associated with the {@link #fileSet}.
     */
-   void setFileList(Set<String> fl)
+   void setArtifactCol(ArtifactCollection col)
    {
-      fileList = fl;
+      artifactCol = col;
    }
 }

=== modified file 'src/com/goldencode/p2j/convert/AbstractConversionWorker.java'
--- old/src/com/goldencode/p2j/convert/AbstractConversionWorker.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/convert/AbstractConversionWorker.java	2025-05-09 07:59:16 +0000
@@ -3,7 +3,7 @@
 ** Abstract : Base class for pattern workers which need to convert a source
 **            AST to a target AST.
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
 ** 001 ECF 20050429   @20972 Created initial version. Abstracted peer AST
@@ -18,7 +18,9 @@
 ** 006 SVL 20130624          Added support for virtual files.
 ** 007 ECF 20140405          Removed unnecessary static variable for AstManager.
 ** 008 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 009 DDF 20250414          Make use of artifacts instead of the filename.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -74,12 +76,11 @@
 
 package com.goldencode.p2j.convert;
 
-import java.io.*;
 import java.util.logging.*;
 
 import antlr.*;
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
-import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.pattern.*;
 
 /**
@@ -199,37 +200,13 @@
    }
    
    /**
-    * Worker routine to centralize initialization of a new AST that is a 
-    * root node.  A new tree ID is registered in the {@link
-    * com.goldencode.ast.AstManager} and set into the AST.
-    *
-    * @param   ast
-    *          AST which is the root node to be initialized.
-    * @param   file
-    *          The file in which to persist the new AST.
-    * @param   resolver
-    *          The current instance of the {@link
-    *          com.goldencode.p2j.pattern.AstSymbolResolver} in use or
-    *          <code>null</code>.
-    */   
-   public static void initializeRootAst(Aast              ast,
-                                        File              file,
-                                        AstSymbolResolver resolver)
-   {
-      String filename = file.getAbsolutePath();
-      String normal   = Configuration.normalizeFilename(filename);
-
-      initializeRootAst(ast, normal, resolver);
-   }
-
-   /**
     * Worker routine to centralize initialization of a new AST that is a root node.  A new tree
     * ID is registered in the {@link com.goldencode.ast.AstManager} and set into the AST.
     *
     * @param   ast
     *          AST which is the root node to be initialized.
-    * @param   filename
-    *          The file in which to persist the new AST. Can be real or virtual (i.e. the file
+    * @param   artifact
+    *          The artifact in which to persist the new AST. Can be real or virtual (i.e. the artifact
     *          can be never persisted into the file system, the name is used to map some ID to
     *          the AST).
     * @param   resolver
@@ -237,12 +214,12 @@
     *          in use or <code>null</code>.
     */
    public static void initializeRootAst(Aast              ast,
-                                        String            filename,
+                                        Artifact          artifact,
                                         AstSymbolResolver resolver)
    {
       try
       {
-         ast.brainwash(filename);
+         ast.brainwash(artifact);
 
          if (resolver != null)
          {

=== modified file 'src/com/goldencode/p2j/convert/BufferScopeWorker.java'
--- old/src/com/goldencode/p2j/convert/BufferScopeWorker.java	2022-05-16 12:34:34 +0000
+++ new/src/com/goldencode/p2j/convert/BufferScopeWorker.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : BufferScopeWorker.java
 ** Abstract : helper for rule set calculation of record scopes
 **
-** Copyright (c) 2005-2022, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------Description-----------------
 ** 001 GES 20050921   @22789 First version which supports global and
@@ -35,6 +35,7 @@
 **     CA  20220316          The lookup of the scope block in case of a free reference must be done using the 
 **                           buffer's definition, and not the free reference block. 
 **     CA  20220516          Allow re-initialization of the context-local state.
+** 016 DDF 20250414          Make use of artifacts instead of the filename.
 */
 
 /*
@@ -100,7 +101,6 @@
 import com.goldencode.p2j.pattern.*;
 import com.goldencode.p2j.security.*;
 
-
 /**
  * A pattern worker whose purpose is to provide helpers for calculation of
  * all buffers, the blocks to which each buffer is scoped and the list of
@@ -936,7 +936,7 @@
                      Aast defAst = AstSymbolResolver.getAst(getResolver(), astID, true);
                      if (defAst != null)
                      {
-                        if (defAst.getFilename().equals(ast.getFilename()))
+                        if (defAst.getArtifact().equals(ast.getArtifact()))
                         {
                            // we are in the same file... check if we are in the same top-level block
                            Aast tl1 = ExpressionConversionWorker.findTopLevel(ast);

=== modified file 'src/com/goldencode/p2j/convert/ConversionDriver.java'
--- old/src/com/goldencode/p2j/convert/ConversionDriver.java	2025-01-02 14:26:27 +0000
+++ new/src/com/goldencode/p2j/convert/ConversionDriver.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : ConversionDriver.java
 ** Abstract : runs the entire conversion process for a file or set of files
 **
-** Copyright (c) 2006-2024, Golden Code Development Corporation.
+** Copyright (c) 2006-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 GES 20060131   @24171 First version.  Supports:
@@ -144,6 +144,11 @@
 ** 047 OM  20240311          Do not generate the <schema>.meta.xml any more. The information is already
 **                           present in DMO interface annotations.
 ** 048 GES 20241218          Added syntax help and javadoc for the -Z file set support. 
+** 049 GES 20230524          Moved some code to a more generic package.
+**     DDF 20250320          Incorporate usage of artifacts instead of file lists in the conversion process.
+**     DDF 20250407          Refactored ArtifactManager and ArtifactCollection usages.
+**     DDF 20250423          Make use of artifacts instead of filenames.
+**     DDF 20250424          Renamed methods to specify artifacts.
 */
 
 /*
@@ -209,6 +214,7 @@
 
 package com.goldencode.p2j.convert;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.db.*;
@@ -343,23 +349,23 @@
       modes.put("CB", new CvtRunMode(false, false, false, false, false, false, false, true, false));
    }
    
-   /** The list of Java AST (abstract syntax tree) files. */
-   private FileList jasts = null;
-   
-   /** Java ASTs (abstract syntax trees) created during frame generation. */
-   private String[] frames = null;
-   
-   /** Java ASTs (abstract syntax trees) created during menus generation. */
-   private String[] menus = null;
-   
-   /** The list of database schema files. */
-   private FileList dschemas = null;
-   
-   /** The list of database P2O files. */
-   private FileList dp2os = null;
-   
-   /** The list of Progress source file specific P2O files. */
-   private FileList sp2os = null;
+   /** The collection of Java AST (abstract syntax tree) artifacts. */
+   private ArtifactCollection jasts = null;
+   
+   /** Collection of Java ASTs (abstract syntax trees) created during frame generation. */
+   private ArtifactCollection frames = null;
+   
+   /** Collection of Java ASTs (abstract syntax trees) created during menus generation. */
+   private ArtifactCollection menus = null;
+   
+   /** The collection of database schema artifacts. */
+   private ArtifactCollection dschemas = null;
+   
+   /** The collection of database P2O artifacts. */
+   private ArtifactCollection dp2os = null;
+   
+   /** The collection of Progress source file specific P2O artifacts. */
+   private ArtifactCollection sp2os = null;
    
    /**
     * Creates an instance with a specific configuration.
@@ -393,7 +399,7 @@
     * </ul>
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     * @throws   SchemaException          If there is a problem with the schema.
     * @throws   IOException              If there is a problem with I/O.
     */
@@ -406,7 +412,7 @@
       CvtRunMode cmode = (CvtRunMode) mode;
       
       // ensure that downstream processing has the proper list of files
-      convertSourceNamesToAstNames();
+      convertSourceArtifactsToAstArtifacts();
       
       processTrees("Schema Annotations (scan Progress source code)", 
                    "schema/annotations",
@@ -424,7 +430,7 @@
       }
       
       // build Progress source schema list
-      FileList sschemas = convertSourceNamesToDictNames(".schema");
+      ArtifactCollection sschemas = convertSourceArtifactsToDictArtifacts(".schema");
       
       if (!cmode.skipDBSchema)
       {
@@ -437,7 +443,7 @@
       }
       
       // convert source file schema files to P2O (if any exist for these specific source files)
-      if (sschemas.size() > 0)
+      if (!sschemas.isEmpty())
       {
          processTrees("Pre-processing P2O Generation (temp-table schema files)", 
                       "schema/p2o_pre",
@@ -465,7 +471,7 @@
       }
       
       // convert source file schema files to P2O (if any exist for these specific source files)
-      if (sschemas.size() > 0)
+      if (!sschemas.isEmpty())
       {
          processTrees("P2O Post-processing (temp-table schema files)", 
                       "schema/p2o_post",
@@ -496,14 +502,14 @@
       }
       
       // build Progress source P2O list
-      sp2os = convertSourceNamesToDictNames(".p2o");
+      sp2os = convertSourceArtifactsToDictArtifacts(".p2o");
       if (!cmode.omitSchemaBrew)
       {
-         FileList dmos = generateDMOs(cmode.skipDBSchema);
-         brew(dmos, "Data Model Objects (DMOs)");
+         ArtifactCollection dmoArtifacts = generateDMOs(cmode.skipDBSchema);
+         brew(dmoArtifacts, "Data Model Objects (DMOs)");
          
-         FileList pojos = generatePOJOs(cmode.skipDBSchema);
-         brew(pojos, "Plain 0ld Java 0bjects (P0J0s)");
+         ArtifactCollection pojoArtifacts = generatePOJOs(cmode.skipDBSchema);
+         brew(pojoArtifacts, "Plain 0ld Java Objects (P0J0s)");
       }
       
       if (!cmode.skipDBSchema && !cmode.omitSchemaBrew)
@@ -529,7 +535,7 @@
     * </ul>
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     * @throws   SchemaException          If there is a problem with the schema.
     * @throws   IOException              If there is a problem with I/O.
     */
@@ -540,7 +546,7 @@
           IOException
    {
       // ensure that downstream processing has the proper list of files
-      convertSourceNamesToAstNames();
+      convertSourceArtifactsToAstArtifacts();
       
       if (Configuration.getParameter("add-embedded-windows", false))
       {
@@ -609,7 +615,7 @@
     *           The debug level.
     *           
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     */
    static void generateWsdls(int debug)
    throws AstException,
@@ -622,8 +628,9 @@
          astFiles[i] = files.get(i) + ".ast";
       }
       
-      FileList asts = new ExplicitFileList(astFiles);
-      
+      ArtifactCollection asts = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                          (Object) astFiles);
+
       processTrees("Generate WSDL files",
                    "convert/soap_services",
                    codenames,
@@ -640,7 +647,7 @@
     * will either create or update the JAST for that (Sub)AppObject.
     * 
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     */
    protected void generateProxyPrograms()
    throws AstException,
@@ -662,12 +669,13 @@
       };
       
       Set<String> allCvtFiles = new HashSet<>();
-      for (String file : asts.listFilenames())
+      List<String> relativeNames = ArtifactManager.getArtifactsRelativePath(asts);
+      for (String file : relativeNames)
       {
          file = FileOperationsWorker.findFile(file, null);
          allCvtFiles.add(file);
       }
-      allCvtFiles.addAll(Arrays.asList(asts.listFilenames()));
+      allCvtFiles.addAll(relativeNames);
       
       // get the proxy config for each file
       Map<String, Integer> programCounter = new HashMap<>();
@@ -779,39 +787,39 @@
          
          if (!proxyFiles.isEmpty())
          {
-            ExplicitFileList proxyAsts = new ExplicitFileList(Configuration.isCaseSensitive());
-            for (String proxyFile : proxyFiles)
-            {
-               String astFile = FileOperationsWorker.findFile(proxyFile, null);
-               if (astFile == null)
-               {
-                  System.out.println("WARNING: Could not resolve program for proxy file: " + proxyFile);
-                  continue;
-               }
-               proxyAsts.add(astFile + ".ast");
-            }
-            
+            ArtifactCollection proxyAsts =
+                     ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                               Configuration.isCaseSensitive());
+            ArtifactCollection additionalProxyAsts =
+                     ArtifactManager.transformArtifactNames(proxyFiles,
+                                                            ".ast",
+                                                            true);
+            List<ArtifactCollection> artifactCollectionList = new ArrayList<>();
+            artifactCollectionList.add(proxyAsts);
+            artifactCollectionList.add(additionalProxyAsts);
+            ArtifactCollection artifacts = ArtifactManager.mergeArtifactCollections(artifactCollectionList);
+
             processTrees("Annotate Proxy Client Programs - pass #" + pass + " of " + size,
                          "annotations/proxy_programs",
                          codenames,
                          false,
-                         proxyAsts,
+                         artifacts,
                          debug);
 
             processTrees("Generate Proxy Client Programs - pass #" + pass + " of " + size,
                          "convert/proxy_programs",
                          codenames,
                          false,
-                         proxyAsts,
+                         artifacts,
                          debug);
       
-            FileList files = ServiceSupport.getProxyClientPrograms(doneAppObjects);
-            if (files.size() > 0)
+            ArtifactCollection artifactCol = ServiceSupport.getProxyClientPrograms(doneAppObjects);
+            if (!artifactCol.isEmpty())
             {
                ServiceSupport.setBrewProxy(true);
                try
                {
-                  brew(files, "for Proxy Client Programs - pass #" + pass + " of " + size);
+                  brew(artifactCol, "for Proxy Client Programs - pass #" + pass + " of " + size);
                }
                finally
                {
@@ -830,7 +838,7 @@
     * processed downstream.
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     */
    private void generateFrames()
    throws AstException,
@@ -856,7 +864,7 @@
                    
       // add generated JASTs to the overall list (used with brew later)
       JavaPatternWorker.disableNewFileTracking();
-      frames = JavaPatternWorker.getPersistedFiles();
+      frames = JavaPatternWorker.getPersistedArtifacts();
    }
    
    /**
@@ -893,9 +901,11 @@
       for (String schema: schemas)
       {
          String dialects_ = dialectList;
-         String p2oName = config.getSchemaResource(schema, ".p2o");
-         ExplicitFileList files = new ExplicitFileList(new String[] { p2oName });
-         
+         Artifact p2oArtifact = config.getSchemaResource(schema, ".p2o");
+         String[] p2oNameArg = new String[] { p2oArtifact.getRelativePath() };
+         ArtifactCollection artifacts = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                                  (Object) p2oNameArg);
+          
          vars.put("dbName", "\"" + schema + "\"") ;
          
          // for each dialect
@@ -924,7 +934,7 @@
                          ruleset,
                          datanames,
                          true,
-                         files,
+                         artifacts,
                          debug,
                          vars);
          }
@@ -938,12 +948,14 @@
     * @param    skipDBSchema
     *           <code>true</code> if the physical DB schemas are not processed.
     * 
-    * @return   The list of JAST files to be "brewed".
+    * @return   A {@code ArtifactCollection} instance containing artifacts of JAST files to be "brewed".
     *
-    * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   ConfigurationException
+    *           If there is problem reading/parsing the configuration.
+    * @throws   AstException
+    *           If there is a problem when processing ASTs.
     */
-   private FileList generateDMOs(boolean skipDBSchema)
+   private ArtifactCollection generateDMOs(boolean skipDBSchema)
    throws AstException,
           ConfigurationException
    {
@@ -960,7 +972,7 @@
                       debug);
       }
       
-      if (sp2os.size() > 0)
+      if (!sp2os.isEmpty())
       {
          processTrees("Generate Data Model Objects (temp-table DMOs)",
                       "schema/java_dmo",
@@ -972,9 +984,13 @@
       
       // add generated JASTs to the overall list (used with brew later)
       JavaPatternWorker.disableNewFileTracking();
-      String[] dmoList = JavaPatternWorker.getPersistedFiles();
+      ArtifactCollection dmoList = JavaPatternWorker.getPersistedArtifacts();
+      List<String> dmoListNames = ArtifactManager.getArtifactsRelativePath(dmoList);
+      String[] dmoListNamesArray = dmoListNames.toArray(new String[0]);
       
-      return new ExplicitFileList(dmoList);
+      ArtifactCollection artifacts = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                               (Object) dmoListNamesArray);
+      return artifacts;
    }
    
    /**
@@ -984,14 +1000,15 @@
     * @param   skipDBSchema
     *          {@code true} if the physical DB schemas are not processed.
     * 
-    * @return  The list of JAST files to be "brewed" to POJO classes.
+    * @return  A {@code ArtifactCollection} instance containing artifacts of JAST files to be "brewed" to
+    *          POJO classes.
     *
     * @throws  ConfigurationException
     *          If there is problem reading/parsing the configuration.
     * @throws  AstException
-    *          If there is an problem processing ASTs.
+    *          If there is a problem when processing ASTs.
     */
-   private FileList generatePOJOs(boolean skipDBSchema)
+   private ArtifactCollection generatePOJOs(boolean skipDBSchema)
    throws AstException,
           ConfigurationException
    {
@@ -1007,7 +1024,7 @@
                       debug);
       }
       
-      if (sp2os.size() > 0)
+      if (!sp2os.isEmpty())
       {
          processTrees("Generate Plain Old Java Objects (temp-table POJOs)",
                       "schema/java_pojo",
@@ -1019,8 +1036,13 @@
       
       // add generated JASTs to the overall list (used with brew later)
       JavaPatternWorker.disableNewFileTracking();
-      
-      return new ExplicitFileList(JavaPatternWorker.getPersistedFiles());
+
+      ArtifactCollection col = JavaPatternWorker.getPersistedArtifacts();
+      List<String> files = ArtifactManager.getArtifactsRelativePath(col);
+      ArtifactCollection artifacts =
+               ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                         (Object) files.toArray(new String[0]));
+      return artifacts;
    }
    
    /**
@@ -1032,7 +1054,7 @@
     * @throws  ConfigurationException   
     *          If there is problem reading/parsing the configuration.
     * @throws  AstException
-    *          If there is an problem processing ASTs.
+    *          If there is a problem processing ASTs.
     */
    private void generateMenus()
    throws AstException,
@@ -1050,7 +1072,7 @@
                    
       // add generated JASTs to the overall list (used with brew later)
       JavaPatternWorker.disableNewFileTracking();
-      menus = JavaPatternWorker.getPersistedFiles();
+      menus = JavaPatternWorker.getPersistedArtifacts();
    }
    
    /**
@@ -1058,15 +1080,15 @@
     * files).  The output is written into the configured project's output
     * path.
     *
-    * @param    files
-    *           The list of JAST files to process.
+    * @param    artifacts
+    *           The {@code ArtifactCollection} instance containing artifacts of JAST files to process.
     * @param    extra
     *           Text to append to the displayed header.
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     */
-   private void brew(FileList files, String extra)
+   private void brew(ArtifactCollection artifacts, String extra)
    throws ConfigurationException,
           AstException
    {
@@ -1074,28 +1096,28 @@
                    "convert/brew",
                    null,
                    false,
-                   files,
+                   artifacts,
                    debug);
    }
    
    /**
-    * Using the source name list as input, this method creates duplicate
-    * lists with the proper <code>.ast</code> or <code>.jast</code> file
-    * suffixes on each entry.  Appended to this list is the list of Java ASTs
-    * created during frame generation.  The results are saved into the
-    * Java AST name list.
+    * Using the source name {@code ArtifactCollection} as input, this method creates duplicate
+    * {@code ArtifactCollection} instances with artifacts that have the proper <code>.ast</code> \
+    * or <code>.jast</code> file suffixes on each entry. Appended to this collection is the collection
+    * of Java ASTs created during frame generation. The results are saved into the Java AST name artifact
+    * collection.
     */
    private void convertSourceNamesToJavaAstNames()
    {
-      List<String> oldjasts = new ArrayList<>();
+      List<ArtifactCollection> oldjasts = new ArrayList<>();
 
       // merge the lists
-      oldjasts.addAll(Arrays.asList(source.withSuffix(".jast")));
-      oldjasts.addAll(Arrays.asList(frames));
-      oldjasts.addAll(Arrays.asList(menus));
-      
+      oldjasts.add(ArtifactManager.transformArtifactNames(source, ".jast", false));
+      oldjasts.add(frames);
+      oldjasts.add(menus);
+
       // save the results
-      jasts = new ExplicitFileList(oldjasts.toArray(new String[0]));
+      jasts = ArtifactManager.mergeArtifactCollections(oldjasts);
    }
    
    /**
@@ -1171,9 +1193,9 @@
          {
             return run;
          }
-         
+
          CvtRunMode cvt = (CvtRunMode) run;
-         
+
          return new CvtRunMode(rm.front,
                                rm.preproc,
                                rm.schema,

=== modified file 'src/com/goldencode/p2j/convert/GenerateLegacyProxyOpenClient.java'
--- old/src/com/goldencode/p2j/convert/GenerateLegacyProxyOpenClient.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/convert/GenerateLegacyProxyOpenClient.java	2025-05-09 07:59:16 +0000
@@ -3,12 +3,15 @@
 ** Abstract : Generate the Java proxy programs for the .xpxg configured programs, associated with legacy 
 **            OpenClient proxies.
 **
-** Copyright (c) 2022-2023, Golden Code Development Corporation.
+** Copyright (c) 2022-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------------Description---------------------------------------
 ** 001 CA  20220516 Created initial version.
 **     CA  20220517 Generate proxy OpenClient programs only for programs part of the initial conversion list. 
 ** 002 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
+** 003 GES 20230524 Moved some code to a more generic package.
+**     DDF 20250320 Incorporate usage of artifacts instead of file lists.
+**     DDF 20250414 Made use of a lambda to generalize the process in the artifact manager.
 */
 
 /*
@@ -70,11 +73,12 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.*;
+import com.goldencode.artifacts.ArtifactResolver;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.pattern.*;
 import com.goldencode.p2j.schema.SchemaException;
-import com.goldencode.p2j.util.*;
 
 /**
  * Given the list of .xpxg files configured in {@code p2j.cfg.xml} under the {@code proxy-programs-cfg} 
@@ -156,11 +160,15 @@
          System.exit(-1);
       }
       
-      ExplicitFileList files = new ExplicitFileList(Configuration.isCaseSensitive());
+      ArtifactCollection origArtifacts =
+               ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                         Configuration.isCaseSensitive());
+      List<ArtifactCollection> col = new ArrayList<>();
+      col.add(origArtifacts);
       for (LegacyProxyConfig cfg : cfgs)
       {
-         List<String> programFiles = cfg.getProxyProgramFiles();
-         for (String program : programFiles)
+         List<String> pfiles = cfg.getProxyProgramFiles();
+         ArtifactResolver<String> operation = (program) ->
          {
             try
             {
@@ -168,26 +176,40 @@
                
                if (pfile == null)
                {
-                  System.out.println("WARNING: Could not find program '" + program + "' for proxy config: " + 
-                                     cfg.getAppObject() + " in package " + cfg.getPkg());
-                  continue;
+                  LOG.log(Level.WARNING, "Could not find program '" +
+                                         program +
+                                         "' for proxy config: " +
+                                         cfg.getAppObject() +
+                                         " in package " +
+                                         cfg.getPkg());
                }
-
-               System.out.println("Adding program '" + pfile + "' for proxy config: " + cfg.getAppObject() + 
-                                  " in package " + cfg.getPkg());
-               files.add(pfile);
+               
+               LOG.log(Level.INFO, "Adding program '" +
+                                   pfile +
+                                   "' for proxy config: " +
+                                   cfg.getAppObject() +
+                                   " in package " +
+                                   cfg.getPkg());
+               
+               return pfile;
             }
             catch (ConfigurationException e)
             {
                LOG.log(Level.SEVERE, "", e);
                System.exit(-2);
             }
-         }
+            
+            return null;
+         };
+         
+         col.add(ArtifactManager.createArtifacts(pfiles, operation));
       }
 
+      ArtifactCollection artifacts = ArtifactManager.mergeArtifactCollections(col);
+
       int debug = MSG_STATUS;
       RunMode run = new CvtRunMode(true, true, true, false, true, false, false, true, true);
-      JobDefinition job = new JobDefinition(run, null, files, null, 1, true, false, debug);
+      JobDefinition job = new JobDefinition(run, null, artifacts, null, 1, true, false, debug);
       GenerateLegacyProxyOpenClient gen = new GenerateLegacyProxyOpenClient(job);
       gen.executeJob(TITLE);
    }

=== modified file 'src/com/goldencode/p2j/convert/IndexSelectionWorker.java'
--- old/src/com/goldencode/p2j/convert/IndexSelectionWorker.java	2025-03-19 13:44:21 +0000
+++ new/src/com/goldencode/p2j/convert/IndexSelectionWorker.java	2025-05-09 07:59:16 +0000
@@ -73,7 +73,8 @@
 ** 028 AP  20240807          Improved index selection for the case when wholeIndex flag is true, but
 **                           further selection can be performed in order to avoid returning WHOLE-INDEX.
 ** 029 CA  20250319          This worker can be used before the 'qualified_oo_name' is set in annotation 
-**                           (like in schema conversion phase), so ensure we can load the ClassDefinition. 
+**                           (like in schema conversion phase), so ensure we can load the ClassDefinition.
+** 030 DDF 20250414          Make use of artifacts instead of the filename.
 */
 
 /*
@@ -134,6 +135,7 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.schema.*;
 import com.goldencode.p2j.security.*;
@@ -190,8 +192,8 @@
    public void visitAst(Aast ast)
    {
       ClassDefinition clsDef = SymbolResolver.resolveClassDefinition(ast);
-      String sourceFile = ast.getFilename();
-      P2OLookup.loadTemporarySchema(sourceFile, clsDef);
+      Artifact sourceArtifact = ast.getArtifact();
+      P2OLookup.loadTemporarySchema(sourceArtifact, clsDef);
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/convert/NameConverterWorker.java'
--- old/src/com/goldencode/p2j/convert/NameConverterWorker.java	2025-04-01 15:05:08 +0000
+++ new/src/com/goldencode/p2j/convert/NameConverterWorker.java	2025-05-09 07:59:16 +0000
@@ -63,6 +63,7 @@
 ** 020 CA  20241119          A DMO alias must not emit as a FQL reserved keyword.
 ** 021 CA  20250401          Any possible Java import from core FWD packages can result in a 'schemaconflict',
 **                           so the DMO name will be qualified if they match a simple core class name.
+** 022 DDF 20250414          Make use of artifacts instead of the filename.
 */
 
 /*
@@ -122,13 +123,14 @@
 
 import java.io.*;
 import java.util.*;
+
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.persist.*;
 import com.goldencode.p2j.persist.orm.*;
 import com.goldencode.p2j.schema.*;
 import com.goldencode.p2j.security.*;
 import com.goldencode.util.*;
-import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.pattern.*;
 
 /**
@@ -231,15 +233,16 @@
    @Override
    public void visitAst(Aast ast)
    {
-      String filename = ast.getFilename();
-      if (filename == null)
+      Artifact artifact = ast.getArtifact();
+      String filename = null;
+      if (artifact == null)
       {
          // this is the dynamic conversion case. The database/source is not available.
          filename = "";
       }
       else
       {
-         filename = filename.toLowerCase();
+         filename = artifact.getRelativePath().toLowerCase();
       }
       
       WorkArea wa = context.get();
@@ -299,7 +302,7 @@
       }
       
       Aast ast = getResolver().getSourceAst();
-      String filename = ast.getFilename().toLowerCase();
+      String filename = ast.getArtifact().getRelativePath().toLowerCase();
       List<String> list = wa.history.get(filename);
       Object[] args =
       {
@@ -483,7 +486,7 @@
          if (type == TYPE_TABLE) // this includes SEQUENCES, too
          {
             Aast ast = getResolver().getSourceAst();
-            String filename = ast.getFilename().toLowerCase();
+            String filename = ast.getArtifact().getRelativePath().toLowerCase();
             Set<String> tableSet = wa.tables.get(filename);
             if (tableSet.contains(result))
             {

=== modified file 'src/com/goldencode/p2j/convert/ProgressTransformDriver.java'
--- old/src/com/goldencode/p2j/convert/ProgressTransformDriver.java	2021-09-29 21:19:47 +0000
+++ new/src/com/goldencode/p2j/convert/ProgressTransformDriver.java	2025-05-09 07:59:16 +0000
@@ -2,13 +2,14 @@
 ** Module   : ProgressTransformDriver.java
 ** Abstract : runs the Progress 4GL to Progress 4GL transformation for a file or set of files
 **
-** Copyright (c) 2018-2021, Golden Code Development Corporation.
+** Copyright (c) 2018-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
 ** 001 GES 20181023 First version.
 ** 002 ECF 20190221 Added blacklist (filespec + ignore list) mode.
 ** 003 CA  20200412 Added incremental conversion support.
 **     OM  20210923 Added -P<name> command line option for specifying the configuration profile.
+** 004 DDF 20250424 Renamed methods to specify artifacts.
 */
 
 /*
@@ -191,7 +192,7 @@
       }
       
       // ensure that downstream processing has the proper list of files
-      convertSourceNamesToAstNames();
+      convertSourceArtifactsToAstArtifacts();
       
       processTrees("User-Defined Transformation", 
                    job.transform,
@@ -213,7 +214,7 @@
           IOException
    {      
       // ensure that downstream processing has the proper list of files
-      convertSourceNamesToAstNames();
+      convertSourceArtifactsToAstArtifacts();
       
       processTrees("Anti-Parsing",
                    "progress/anti_parser",

=== modified file 'src/com/goldencode/p2j/convert/TransformDriver.java'
--- old/src/com/goldencode/p2j/convert/TransformDriver.java	2023-05-10 06:23:45 +0000
+++ new/src/com/goldencode/p2j/convert/TransformDriver.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : TransformDriver.java
 ** Abstract : common code for transformation of a file or set of files
 **
-** Copyright (c) 2006-2023, Golden Code Development Corporation.
+** Copyright (c) 2006-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------------Description---------------------------------------
 ** 001 GES 20181022 Extracted common code from ConversionDriver.
@@ -44,6 +44,13 @@
 ** 008 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
 ** 009 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                  versions, based on node types rather on string paths.
+** 010 GES 20230524 Moved some code to a more generic package.
+**     DDF 20250320 Incorporated usage of artifacts instead of file sets.
+**     DDF 20250407 Refactored ArtifactManager and ArtifactCollection usages. Removed unused
+**                  ExcludeCheck and ScanCheck.
+**     DDF 20250414 Make use of artifacts instead of the filename.
+**     DDF 20250423 Replace mode filename usages with artifacts.
+**     DDF 20250424 Renamed methods to specify artifacts.
 */
 
 /*
@@ -112,12 +119,12 @@
 import java.util.logging.*;
 import java.text.*;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.util.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.db.*;
 import com.goldencode.p2j.uast.*;
-import com.goldencode.p2j.util.*;
 import com.goldencode.p2j.pattern.*;
 import com.goldencode.p2j.schema.*;
 
@@ -147,12 +154,9 @@
    /** Chars that must be escaped to eliminate regular expression meaning. */
    protected static final char[] DANGER_LIST = { '{', '}', '[', ']', '(', ')' };
    
-   /** Form in which to obtain the list of files. */
-   protected static enum ListType { CMD_LINE, WHITELIST, BLACKLIST, FILESPEC, FILESET };
-   
    /** Logger */
    private static final ConversionStatus LOG = ConversionStatus.get(TransformDriver.class);
-   
+
    static
    {
       try
@@ -166,17 +170,17 @@
       }
    }
    
-   /** The list of Progress source files (command line input). */
-   protected FileList source = null;
-   
-   /** The list of Progress AST (abstract syntax tree) files. */
-   protected FileList asts = null;
-   
-   /** The list of database dictionary files. */
-   protected FileList dicts = null;
-   
-   /** The list of Progress source file specific dictionary files. */
-   protected FileList sdicts = null;
+   /** The collection of Progress source artifacts (command line input). */
+   protected ArtifactCollection source = null;
+   
+   /** The collection of Progress AST (abstract syntax tree) artifacts. */
+   protected ArtifactCollection asts = null;
+   
+   /** The collection of database dictionary artifacts. */
+   protected ArtifactCollection dicts = null;
+   
+   /** The collection of Progress source artifacts specific to dictionary artifacts. */
+   protected ArtifactCollection sdicts = null;
    
    /** The debug level to honor throughout the process. */
    protected int debug = MSG_STATUS;
@@ -198,7 +202,7 @@
     */
    public TransformDriver(JobDefinition job)
    {
-      this.source = job.files;
+      this.source = job.artifacts;
       this.mode   = job.mode;
       this.job    = job;
       
@@ -235,7 +239,7 @@
     * </ul>
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     * @throws   SchemaException          If there is a problem with the schema.
     * @throws   IOException              If there is a problem with I/O.
     * @throws   Exception                For any other problem.
@@ -258,7 +262,7 @@
       runScanDriver(mode.preproc, mode.schema, job.abortOnError, job.threads);
       
       // ensure that downstream processing has the proper list of files
-      convertSourceNamesToAstNames();
+      convertSourceArtifactsToAstArtifacts();
       
       processTrees("Post-Parse Fixups",
                    "fixups/post_parse_fixups",
@@ -284,8 +288,8 @@
       if (mode.schema)
       {
          // clean up previous run - build schema list and delete those files
-         FileList delDicts = createDatabaseList(".schema", false);
-         deleteFiles(delDicts.listFilenames());
+         ArtifactCollection delDicts = createDatabaseList(".schema", false);
+         ArtifactManager.deleteArtifacts(delDicts);
          
          // build dictionary list
          dicts = createDatabaseList(".dict", omitSchemaMetadata());
@@ -299,11 +303,11 @@
                       debug);
          
          // build schema list
-         sdicts = convertSourceNamesToDictNames(".dict");
+         sdicts = convertSourceArtifactsToDictArtifacts(".dict");
          
          // fixup source file schema files (if any exist for these specific
          // source files)
-         if (sdicts.size() > 0)
+         if (!sdicts.isEmpty())
          {
             processTrees("Schema Fixups (Progress source file schemas)",
                          "schema/fixups",
@@ -328,18 +332,16 @@
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
     * @throws   SchemaException          If there is a problem with the schema.
-    * @throws   IOException              If there is a problem with I/O.
     */
    protected void runSchemaLoader()
    throws ConfigurationException,
-          SchemaException,
-          IOException
+          SchemaException
    {
       printHeader("SchemaLoader", debug);
       
       // clean up previous run - build dictionary list and delete those files
-      FileList delDicts = createDatabaseList(".dict", false);
-      deleteFiles(delDicts.listFilenames());
+      ArtifactCollection delDicts = createDatabaseList(".dict", false);
+      ArtifactManager.deleteArtifacts(delDicts);
       
       SchemaLoader loader = new SchemaLoader();
       
@@ -358,7 +360,7 @@
     *
     * @param    preproc
     *           <code>true</code> if the <code>Preprocessor</code> should
-    *           should be forced to run.  <code>false</code> if previously
+    *           be forced to run.  <code>false</code> if previously
     *           cached preprocessor results should be honored.
     * @param    schema
     *           <code>true</code> if the <code>SchemaLoader</code> and
@@ -370,7 +372,7 @@
     *           else <code>false</code>.
     * @param    threads
     *           Number of threads to use for the scan. A non-positive value will enable
-    *           the default behavior of a work-stealing thread pool with one thread per
+    *           the default behavior of a work-stealing thread pool with one thread
     *           per available CPU. A positive value will create a fixed size thread pool.
     *
     * @throws   Exception                If there is a problem.
@@ -408,11 +410,8 @@
     * @param    schema
     *           <code>true</code> means that schema related generated files
     *           should be deleted.
-    *
-    * @throws   IOException              If there is a problem with I/O.
     */
    protected void cleanScanResults(boolean preproc, boolean schema)
-   throws IOException
    {
       ArrayList<String> exts = new ArrayList<>();
       
@@ -441,36 +440,10 @@
       String[] extslist = exts.toArray(new String[0]);
       
       // do the work
-      zapFiles(source.listFilenames(), extslist);
+      ArtifactManager.zapFiles(source, extslist);
       if (!job.incremental)
       {
-         zapFiles(SymbolResolver.listFakeoutFiles(), extslist);
-      }
-   }
-   
-   /**
-    * Concatenate all extensions with all the files and if any resulting
-    * names exist, delete the matching file from the file system.
-    *
-    * @param    files
-    *           The list of file base names.
-    * @param    extslist
-    *           The list of extensions to zap.
-    */
-   protected void zapFiles(String[] files, String[] extslist)
-   {
-      for (int i = 0; i < files.length; i++)
-      {
-         for (int j = 0; j < extslist.length; j++)
-         {
-            File check = new File(files[i] + extslist[j]);
-            
-            if (check.exists() && check.isFile())
-            {
-               // delete the file
-               check.delete();
-            }
-         }
+        ArtifactManager.zapFiles(SymbolResolver.listFakeoutArtifacts(), extslist);
       }
    }
    
@@ -524,7 +497,7 @@
     * root nodes list to create a call graph for each root node.
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     * @throws   IOException              If there is a problem with I/O.
     */
    protected void runCallGraphGenerator()
@@ -557,8 +530,8 @@
     *           project default.
     * @param    db
     *           Load default database name mappings.
-    * @param    files
-    *           The list of ASTs to process.
+    * @param    artifacts
+    *           The collection of AST artifacts to process.
     * @param    debug
     *           The debug level to use with the pattern engine.
     * 
@@ -566,14 +539,14 @@
     *           {@link PatternEngine#getNewProgramFiles()}.
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     */
-   protected static List<String> processTrees(String   section,
-                                              String   rules,
-                                              String   matches,
-                                              boolean  db,
-                                              FileList files,
-                                              int      debug)
+   protected static List<String> processTrees(String             section,
+                                              String             rules,
+                                              String             matches,
+                                              boolean            db,
+                                              ArtifactCollection artifacts,
+                                              int                debug)
    throws ConfigurationException,
           AstException
    {
@@ -584,7 +557,7 @@
       
       // create a new pattern engine, configure it and run
       PatternEngine engine = new PatternEngine();
-      engine.addAstSpec(files);
+      engine.addAstSpec(artifacts);
       PatternEngine.setDebugLevel(debug);
       engine.run(rules);
       
@@ -606,8 +579,8 @@
     *          project default.
     * @param   db
     *          Load default database name mappings.
-    * @param   files
-    *          The list of ASTs to process.
+    * @param   artifacts
+    *          The collection of AST artifacts to process.
     * @param   debug
     *          The debug level to use with the pattern engine.
     * @param   vars
@@ -618,13 +591,13 @@
     * @throws  AstException
     *          If there is a problem processing ASTs.
     */
-   protected void processTrees(String   section,
-                               String   rules,
-                               String   matches,
-                               boolean  db,
-                               FileList files,
-                               int      debug,
-                               Map      vars)
+   protected void processTrees(String             section,
+                               String             rules,
+                               String             matches,
+                               boolean            db,
+                               ArtifactCollection artifacts,
+                               int                debug,
+                               Map                vars)
    throws ConfigurationException,
           AstException
    {
@@ -635,74 +608,13 @@
       
       // create a new pattern engine, configure it and run
       PatternEngine engine = new PatternEngine();
-      engine.addAstSpec(files);
+      engine.addAstSpec(artifacts);
       PatternEngine.setDebugLevel(debug);
       engine.setVariableInitializers(vars);
       engine.run(rules);
    }
    
    /**
-    * Delete the file associated with each name in the list.
-    *
-    * @param   list
-    *          List of explicit filenames to delete.
-    *
-    * @throws  IOException
-    *          If there is a problem with I/O.
-    */
-   protected void deleteFiles(String[] list)
-   throws IOException
-   {
-      for (int i = 0; i < list.length; i++)
-      {
-         File next = new File(list[i]);
-         next.delete();
-      }
-   }   
-   
-   /**
-    * For each element in the spec list, generate a list of matching files
-    * (each spec may contain traditional file system wildcard characters)
-    * and delete each of these files.  The given <code>ExcludeCheck</code> 
-    * instance is used to remove results from the list on a callback basis.
-    *
-    * @param   speclist
-    *          List of file specifications.
-    * @param   check
-    *          If <code>null</code>, no callback occurs.  Otherwise, the 
-    *          <code>exclude</code> method will be called on the passed instance.
-    *
-    * @throws  IOException
-    *          If there is a problem with I/O.
-    */
-   protected void deleteFileSpecs(String[] speclist, ExcludeCheck check)
-   throws IOException
-   {
-      for (int i = 0; i < speclist.length; i++)
-      {
-         int    idx  = speclist[i].lastIndexOf(File.separator);
-         String dir  = (idx < 0) ? "." : speclist[i].substring(0, idx);
-         String spec = speclist[i].substring(idx + 1);
-         
-         // create a list of actual files matching this spec
-         FileList list = new FileSpecList(new File(dir), spec, false);
-         File[]   fset = list.list();
-         
-         for (int j = 0; j < fset.length; j++)
-         {
-            // prune out the preprocessor cache files
-            if (check != null && check.exclude(fset[j].getCanonicalPath()))
-            {
-               continue;
-            }
-            
-            // delete the file
-            fset[j].delete();
-         }
-      }
-   }
-   
-   /**
     * Print a section header and optionally spacers, depending on debug
     * level (if level &gt; <code>MSG_NONE</code> then spacers are printed).
     *
@@ -734,28 +646,24 @@
     * suffixes on each entry. The results are saved into the Progress AST
     * name list.
     */
-   protected void convertSourceNamesToAstNames()
+   protected void convertSourceArtifactsToAstArtifacts()
    {
-      List<String> astFiles = new ArrayList<>();
-      
-      astFiles.addAll(Arrays.asList(source.withSuffix(".ast")));
-      
-      asts = new ExplicitFileList(astFiles.toArray(new String[0]));
+      asts = ArtifactManager.transformArtifactNames(source, ".ast", false);
       Set<String> files = new LinkedHashSet<>();
-      
-      // preserve the order as listed by FileList
-      files.addAll(Arrays.asList(asts.listFilenames()));
+
+      // preserve the order as listed by the ArtifactCollection
+      files.addAll(ArtifactManager.getArtifactsRelativePath(asts));
       
       // get the legacy class hierarchy and create a BFS walk.  these will be processed further
       // in this order
-      List<String> classes = sortLegacyClasses(files);
+      List<String> classes = sortLegacyClasses(asts);
       
       List<String> sorted = new ArrayList<>(classes);
       files.removeAll(classes);
       sorted.addAll(files);
-      
-      asts = new ExplicitFileList(sorted.toArray(new String[0]));
-      ((ExplicitFileList) asts).setSort(false);
+
+      // TODO: Important to check, there might be a case where the artifact is replaced
+      asts = ArtifactManager.getArtifactsByName(sorted);
    }
    
    /**
@@ -774,13 +682,9 @@
     * @param   omitMeta
     *          {@code true} to omit the metadata filename from the resulting list; {@code false} to retain it.
     *
-    * @return  The list of files found.
-    *
-    * @throws  ConfigurationException
-    *          If there is problem reading/parsing the configuration.
+    * @return  The collection of artifacts found.
     */
-   protected FileList createDatabaseList(String extension, boolean omitMeta)
-   throws ConfigurationException
+   protected ArtifactCollection createDatabaseList(String extension, boolean omitMeta)
    {
       Configuration cfg = Configuration.getInstance();
       SchemaConfig config = Configuration.getSchemaConfig();
@@ -804,11 +708,11 @@
                   return;
                }
                
-               String fileName = config.getSchemaFileName(schema, extension);
-               if (new File(fileName).exists() && !toReturn.contains(fileName))
+               Artifact fileArtifact = config.getSchemaFileName(schema, extension);
+               if (fileArtifact.exists() && !toReturn.contains(fileArtifact.getRelativePath()))
                {
                   // collect ONLY existing files
-                  toReturn.add(fileName);
+                  toReturn.add(fileArtifact.getRelativePath());
                }
             });
             continue;
@@ -820,15 +724,18 @@
             continue;
          }
          
-         String fileName = config.getSchemaFileName(schema, extension);
-         if (new File(fileName).exists() && !toReturn.contains(fileName))
+         Artifact fileArtifact = config.getSchemaFileName(schema, extension);
+         if (fileArtifact.exists() && !toReturn.contains(fileArtifact.getRelativePath()))
          {
             // collect ONLY existing files
-            toReturn.add(fileName);
+            toReturn.add(fileArtifact.getRelativePath());
          }
       }
-      
-      return new ExplicitFileList(toReturn.toArray(new String[toReturn.size()]));
+
+      String[] toReturnArray = toReturn.toArray(new String[0]);
+      ArtifactCollection artifacts = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                               (Object) toReturnArray);
+      return artifacts;
    }
    
    /**
@@ -851,11 +758,11 @@
     *           The file extension to search for (usually <code>.dict</code>
     *           or <code>.schema</code>).
     *
-    * @return   The list of files found.
+    * @return   The collection of artifacts found.
     */
-   protected FileList convertSourceNamesToDictNames(String suffix)
+   protected ArtifactCollection convertSourceArtifactsToDictArtifacts(String suffix)
    {
-      return new ExplicitFileList(source.withSuffix(suffix));
+      return ArtifactManager.transformArtifactNames(source, suffix, false);
    }
    
    /**
@@ -922,32 +829,41 @@
             
             ConversionData.connect();
             
-            // filter the files and keep only the following:
-            // - newly added files
-            // - changed files
-            String[] files = job.files.listFilenames();
-            for (int i = 0; i < files.length; i++)
-            {
-               files[i] = Configuration.normalizeFilename(files[i]);
-            }
-            Set<String> filteredFiles = new LinkedHashSet<>();
-            Set<String> dependencies = new LinkedHashSet<>();
-            dependencies.addAll(Arrays.asList(files));
-            
-            Set<String> depFiles = new HashSet<>();
+            // filter the artifacts and keep only the following:
+            // - newly added artifacts
+            // - changed artifacts
+            ArtifactCollection files = job.artifacts;
+            ArtifactCollection filteredFiles = new ArtifactCollection();
+            ArtifactCollection dependencies = new ArtifactCollection(files);
             boolean force = false;
-            
-            do
+
+            // Used to keep track of artifacts and avoid duplicates
+            Set<Artifact> processedArtifacts = new HashSet<>(files.getArtifacts());
+
+            while (!dependencies.isEmpty())
             {
-               depFiles.clear();
-               for (String file : dependencies)
+               ArtifactCollection newDependencies = new ArtifactCollection();
+               ArtifactCollection newFilteredFiles = new ArtifactCollection();
+               Iterator<Artifact> it = dependencies.iterator();
+               while (it.hasNext())
                {
-                  if (force || ConversionData.mustConvertFile(file))
+                  Artifact artifact = it.next();
+                  String file = artifact.getRelativePath();
+                  if (force || ((BaseArtifact) artifact).mustConvert())
                   {
                      System.out.println(file + " - added for conversion" + (force ? " (dependency)" : ""));
-                     filteredFiles.add(file);
-                     
-                     depFiles.addAll(ConversionData.clean(file));
+                     newFilteredFiles.add(artifact);
+
+                     ArtifactCollection depArtifacts = ArtifactManager.cleanArtifact(artifact);
+                     Iterator<Artifact> depIter = depArtifacts.iterator();
+                     while (depIter.hasNext())
+                     {
+                        Artifact dep = depIter.next();
+                        if (processedArtifacts.add(dep))
+                        {
+                           newDependencies.add(dep);
+                        }
+                     }
                   }
                   else
                   {
@@ -955,17 +871,12 @@
                   }
                }
                
-               depFiles.removeAll(filteredFiles);
-               
-               dependencies.clear();
-               dependencies.addAll(depFiles);
-               
-               filteredFiles.addAll(depFiles);
+               dependencies = newDependencies;
+               filteredFiles.addAll(newFilteredFiles);
                
                force = true;
             }
-            while (!depFiles.isEmpty());
-            
+
             ConversionData.disconnect();
             
             if (filteredFiles.isEmpty())
@@ -974,9 +885,8 @@
                return;
             }
             
-            job.files = new ExplicitFileList(filteredFiles.toArray(new String[0]), 
-                                             job.files.isCaseSensitive());
-            source = job.files;
+            job.artifacts = filteredFiles;
+            source = job.artifacts;
          }
          else
          {
@@ -1022,7 +932,7 @@
     * that is already defined in this instance.
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     * @throws   SchemaException          If there is a problem with the schema.
     * @throws   IOException              If there is a problem with I/O.
     */
@@ -1037,7 +947,7 @@
     * that is already defined in this instance.
     *
     * @throws   ConfigurationException   If there is problem reading/parsing the configuration.
-    * @throws   AstException             If there is an problem processing ASTs.
+    * @throws   AstException             If there is a problem processing ASTs.
     * @throws   SchemaException          If there is a problem with the schema.
     * @throws   IOException              If there is a problem with I/O.
     */
@@ -1048,75 +958,6 @@
           IOException;
    
    /**
-    * Enables delegation of list processing to another method while providing 
-    * a callback to determine if a given list entry needs to be excluded from
-    * processing.
-    */
-   protected abstract class ExcludeCheck
-   {
-      /**
-       * Determines if a given name should be excluded from list processing.
-       *
-       * @param    name
-       *           The list element to check.
-       *
-       * @return   <code>true</code> if the element should be excluded from
-       *           list processing.
-       */
-      abstract boolean exclude(String name);
-   }
-   
-   /**
-    * Helper to only exclude the generated preprocessor files (cache and 
-    * preprocessor hints) conditionally and to always exclude the manually
-    * coded hints files.
-    */
-   protected class ScanCheck
-   extends ExcludeCheck
-   {
-      /** <code>true</code> if preprocessor files should be deleted. */
-      protected boolean preproc = false;
-      
-      /** <code>true</code> if schema files should be deleted. */
-      protected boolean schema = false;
-      
-      /**
-       * Construct and instance and store the preprocessor delete setting.
-       *
-       * @param    preproc
-       *           <code>true</code> if preprocessor files should be deleted.
-       * @param    schema
-       *           <code>true</code> if schema files should be deleted.
-       */
-      ScanCheck(boolean preproc, boolean schema)
-      {
-         this.preproc = preproc;
-         this.schema  = schema;
-      }
-      
-      /**
-       * Determines if a given name should be excluded from list processing. 
-       *
-       * @param    name
-       *           The list element to check.
-       *
-       * @return   <code>true</code> if the element should be excluded from
-       *           list processing.
-       */
-      boolean exclude(String name)
-      {
-         return (!preproc && name.endsWith(".cache"))           ||
-                (!preproc && name.endsWith(".pphints"))         ||
-                (!schema  && name.endsWith(".dict"))            ||
-                (!schema  && name.endsWith(".schema"))          ||
-                (!schema  && name.endsWith(".schema.original")) ||
-                (!schema  && name.endsWith(".p2o"))             ||
-                name.endsWith(".schema.hints")                  ||
-                name.endsWith(".hints");
-      }
-   }
-   
-   /**
     * Interpret the mode command line argument and convert this into the
     * effective run mode object.  Multiple "+" delimited mode values can
     * be included in the same argument.  In this case, the result is the
@@ -1221,8 +1062,8 @@
       /** Flag indicating if the incremental conversion mode is on. */
       public final boolean incremental;
       
-      /** 4GL source files to process. */
-      public /*final*/ FileList files;
+      /** Collection of 4GL source artifacts to process. */
+      public ArtifactCollection artifacts;
       
       /** Debug level. */
       public final int debug;
@@ -1253,8 +1094,8 @@
        *          Defines the features of this job.
        * @param   transform
        *          User-defined rule-set.
-       * @param   files
-       *          The 4GL source files to process.
+       * @param   artifacts
+       *          The collection of 4GL source artifacts to process.
        * @param   cfgProfiles
        *          The optional configuration profiles.
        * @param   threads
@@ -1268,7 +1109,7 @@
        */
       protected JobDefinition(RunMode run,
                               String transform,
-                              FileList files,
+                              ArtifactCollection artifacts,
                               List<String> cfgProfiles,
                               int threads,
                               boolean abortOnError,
@@ -1277,7 +1118,7 @@
       {
          this.mode = run;
          this.transform = transform;
-         this.files = files;
+         this.artifacts = artifacts;
          this.cfgProfiles = cfgProfiles == null || cfgProfiles.isEmpty() ? null : new ArrayList<>(cfgProfiles);
          this.threads = threads;
          this.abortOnError = abortOnError;
@@ -1291,14 +1132,14 @@
     * <p>
     * The program will return a non-zero code if there is a fatal error during the transformation processing.
     * Each child class implements its own command line syntax, but the options, modes and 4GL source file
-    * lists are handled in a common way.  The mode names are driver- specific, however the various front end
+    * lists are handled in a common way.  The mode names are driver-specific, however the various front end
     * modes are common since all drivers need to be able to create ASTs.
     * <p>
     * Syntax:
     * <pre>{@code java -DP2J_HOME=<home> <TransformDriverChildClass> [-options]* <mode> [user_defined_rule_set] <file-specs>}</pre>
     * Where:
     * <ul>
-    *   <li>Options (F, S, and X are mutually exclusive. If none is specified <file-specs> is a list of files
+    *   <li>Options (F, S, and X are mutually exclusive. If none is specified {@code file-specs} is a list of files
     *       added to command line):
     *       <ul>
     *          <li>Dn = set debug level 'n' (must be a numeric digit between {@code MSG_NONE} (0) and 
@@ -1324,7 +1165,7 @@
     *   <li>{@code mode} (one or more of the following values, use '+' to delimit if there are two or more
     *       values; don't insert spaces!)
     *   <li>The optional {@code user_defined_rule_set} specifies the user-defined rule-set filename to execute
-    *        (only some modes support this, see sub-class documentation for details)
+    *        (only some modes support this, see subclass documentation for details)
     *   <li>explicit command line list (default approach if none of S, P, or F are present)
     *       <ul>
     *          <li>{@code <filelist>} is arbitrary list of absolute and/or relative file names to scan, this
@@ -1376,15 +1217,15 @@
    protected static JobDefinition processCommandLine(String[] args, Map<String, RunMode> modes, String[] help) 
    {
       // option defaults
-      ListType type         = ListType.CMD_LINE;
-      boolean  recursion    = true;
-      boolean  abortOnError = false;
-      boolean  dryRun       = false;
-      int      debug        = MSG_STATUS;
-      int      idx          = 0;
-      int      threads      = 1;
-      boolean  incremental  = false;
-      List<String> cfgProfiles = new ArrayList<>();
+      ArtifactManager.ListType type = ArtifactManager.ListType.CMD_LINE;
+      boolean  recursion            = true;
+      boolean  abortOnError         = false;
+      boolean  dryRun               = false;
+      int      debug                = MSG_STATUS;
+      int      idx                  = 0;
+      int      threads              = 1;
+      boolean  incremental          = false;
+      List<String> cfgProfiles      = new ArrayList<>();
       
       // requires (mode + directory + filespec) OR (mode and a filelist) OR (mode), with the file-set read from
       // p2j.cfg.xml
@@ -1415,19 +1256,19 @@
          }
          if (opts.indexOf('s') != -1)
          {
-            type = ListType.FILESPEC;
+            type = ArtifactManager.ListType.FILESPEC;
          }
          if (opts.indexOf('f') != -1)
          {
-            type = ListType.WHITELIST;
+            type = ArtifactManager.ListType.WHITELIST;
          }
          if (opts.indexOf('x') != -1)
          {
-            type = ListType.BLACKLIST;
+            type = ArtifactManager.ListType.BLACKLIST;
          }
          if (opts.indexOf('z') != -1)
          {
-            type = ListType.FILESET;
+            type = ArtifactManager.ListType.FILESET;
          }
          if (opts.indexOf('n') != -1)
          {
@@ -1520,117 +1361,49 @@
       // configuration profiles (without schema) need to be loaded now, as the file-sets are required to be 
       // known bellow.
       Configuration.loadConfigProfiles(cfgProfiles);
-      
-      FileList files = null;
-      
+
       // at this point idx points to the next arg after the options, this
       // must be one of our various forms of filename input
-      switch (type)
-      {
-         case CMD_LINE:
-            if (args.length <= idx && Configuration.getFileList() != null)
-            {
-               // no explicit command-line files are provided and p2j.cfg.xml has configured file-set
-               files = Configuration.getFileList();
-            }
-            
-            if (files == null)
-            {
-               // there must be at least 1 arg left
-               if (args.length <= idx)
-               {
-                  syntax(help, "Missing file list parameter.", -7);
-               }
-               
-               int      len      = args.length;
-               String[] filelist = new String[len - idx];
-               
-               if (len - idx >= 0)
-               {
-                  System.arraycopy(args, idx, filelist, 0, len - idx);
-               }
-               
-               files = new ExplicitFileList(filelist);
-            }
-            
-            break;
-            
-         case WHITELIST:
-            // there must be exactly 1 arg left
-            if ((args.length - idx) != 1)
-            {
-               syntax(help, "Missing custom file list parameter.", -8);
-            }
-            
-            files = FileListFactory.createExplicit(args[idx]);
-            break;
-            
-         case FILESPEC:
-            // there must be exactly 2 args left
-            if ((args.length - idx) != 2)
-            {
-               syntax(help, "Missing/incomplete filespec parameter.", -9);
-            }
-            
-            files = new FileSpecList(new File(args[idx]), args[idx + 1], recursion);
-            break;
-            
-         case BLACKLIST:
-            // there must be exactly 3 args left: starting directory, filespec, ignore filename
-            if ((args.length - idx) != 3)
-            {
-               syntax(help, "Missing/incomplete filespec and exclude list parameters.", -10);
-            }
-            
-            // TODO: we could set case-sensitivity from the legacy system setting, which would allow us to
-            //       read blacklists made with inputs on case-insensitive systems
-            files = FileListFactory.createBlacklist(args[idx],
-                                                    args[idx + 1],
-                                                    recursion, 
-                                                    false, 
-                                                    args[idx + 2]);
-            break;
-         case FILESET:
-            if ((args.length - idx) != 1)
-            {
-               syntax(help, "Missing file set parameter.", -11);
-            }
-
-            FileSet fileSet = FileListFactory.loadFileSet(args[idx]);
-            if (fileSet != null)
-            {
-               try
-               {
-                  files = FileListFactory.processFileSet(fileSet, Configuration.isCaseSensitive());
-               } 
-               catch (IOException e)
-               {
-                  LOG.log(Level.SEVERE, "Processing file set:", e);
-                  return null;
-               }
-            }
-            
-            break;
-      }
-      
-      if (files == null)
-      {
-         return null;
-      }
-      
+      String[] conversionArgs = null;
+      if (idx < args.length)
+      {
+         conversionArgs = Arrays.copyOfRange(args, idx, args.length);
+      }
+
+      ArtifactConversionResult acr = ArtifactManager.createConversionArtifacts(conversionArgs,
+                                                                               type,
+                                                                               recursion,
+                                                                               help);
+      if (acr.isFailed())
+      {
+         int errorCode = acr.getCode();
+         LOG.log(Level.SEVERE, "Artifacts could not be converted, error code " + errorCode);
+         System.exit(errorCode);
+      }
+
+      ArtifactCollection artifacts = acr.getArtifactCollection();
       // if -L option was used, just list files which would be processed to stdout, return null
       if (dryRun)
       {
          LOG.log(Level.CONFIG, "Dry run;  listing source files only...");
-         for (File file : files.list())
+         Iterator<Artifact> it = artifacts.iterator();
+         while (it.hasNext())
          {
-            LOG.log(Level.CONFIG, file.toString());
+            Artifact artifact = it.next();
+            LOG.log(Level.CONFIG, artifact.getRelativePath());
          }
          
          return null;
       }
       
-      return new JobDefinition(run, transform, files, cfgProfiles, threads, abortOnError, incremental, debug);
+      return new JobDefinition(run,
+                               transform,
+                               artifacts,
+                               cfgProfiles,
+                               threads,
+                               abortOnError,
+                               incremental,
+                               debug);
    }
    
    /**
@@ -1643,13 +1416,13 @@
     *           Map of qualified class names to file names.
     * @param    clsHierarchy
     *           Map of qualified class names to a set of subclasses.
-    * @param    file
-    *           The file name to process.
+    * @param    artifact
+    *           The artifact to process.
     */
    private static void processOODefinition(AstManager               mgr,
                                            Map<String, String>      cls2file,
                                            Map<String, Set<String>> clsHierarchy,
-                                           String                   file)
+                                           Artifact                 artifact)
    {
       // temporarily load this AST and get:
       // - the qualified class name
@@ -1657,7 +1430,7 @@
       //   interface)
       // - all implemented interfaces (which we treat the same as a superclass) for a class
       
-      Aast ast    = mgr.loadTree(file);
+      Aast ast    = mgr.loadTree(artifact);
       Aast def    = ast.getImmediateChild(ProgressParserTokenTypes.CLASS_DEF, null);
       Aast header = null;
       
@@ -1704,7 +1477,7 @@
       Aast   sym   = header.getImmediateChild(ProgressParserTokenTypes.SYMBOL, null);
       String qname = sym.getText().toLowerCase();
       
-      cls2file.put(qname, file);
+      cls2file.put(qname, artifact.getRelativePath());
 
       // link the qname into the set for every super class/super interface/implemented interface
       for (String superClass : supers)
@@ -1731,7 +1504,7 @@
     * link them to the root class even if they are not direct children.
     * 
     * @param    ref
-    *           The {@code KW_INHERITS} or {@code KW_IMPLEMTS} node which has one or more class name
+    *           The {@code KW_INHERITS} or {@code KW_IMPLEMENTS} node which has one or more class name
     *           children. 
     * @param    clsHierarchy
     *           The map of subclasses for each parent class.
@@ -1781,18 +1554,20 @@
     * @return   The sources, where classes are at the beginning of the list, in BFS order, with  
     *           the rest of the files following it, in alphabetical order.
     */
-   private static List<String> sortLegacyClasses(Set<String> sources)
+   private static List<String> sortLegacyClasses(ArtifactCollection sources)
    {
       AstManager mgr = AstManager.get();
       
       Map<String, String>      cls2file     = new HashMap<>();
       Map<String, Set<String>> clsHierarchy = new HashMap<>();
       
-      for (String file : sources)
+      Iterator<Artifact> iter = sources.iterator();
+      while(iter.hasNext())
       {
-         if (file.endsWith(".cls.ast"))
+         Artifact artifact = iter.next();
+         if (artifact.endsWith(".cls.ast"))
          {
-            processOODefinition(mgr, cls2file, clsHierarchy, file);
+            processOODefinition(mgr, cls2file, clsHierarchy, artifact);
          }
       }
       
@@ -1817,7 +1592,7 @@
     * Perform a BFS walk of the class hierarchy.
     * 
     * @param    hierarchy
-    *           A map of classes to their direct sub-classes.
+    *           A map of classes to their direct subclasses.
     * @param    parent
     *           The root class to start the walk.
     * 

=== modified file 'src/com/goldencode/p2j/convert/db/AstExternalizble.java'
--- old/src/com/goldencode/p2j/convert/db/AstExternalizble.java	2023-01-27 17:43:20 +0000
+++ new/src/com/goldencode/p2j/convert/db/AstExternalizble.java	2025-05-09 07:59:16 +0000
@@ -2,14 +2,17 @@
 ** Module   : AstExternalizble.java
 ** Abstract : Allow serialization for an AST, to database.
 **
-** Copyright (c) 2020-2023, Golden Code Development Corporation.
+** Copyright (c) 2020-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description----------------------------------
 ** 001 CA  20200412 Created initial version, for incremental conversion support.
 ** 002 CA  20200416 Reduce the memory footprint for incremental conversion.
 ** 003 CA  20210423 Added 'getAstId'.
 ** 004 CA  20230127 Performance improvement for incremental conversion.
+** 005 DDF 20250414 Make use of artifacts instead of the filename.
+**     DDF 20250424 Renamed getTreeName() to getTreeArtifact().
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -67,6 +70,7 @@
 
 import java.io.*;
 
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.pattern.*;
 
@@ -117,16 +121,15 @@
       
       AstManager mgr = AstManager.get();
       long treeId = mgr.getTreeId(astId);
-      String treeName = mgr.getTreeName(treeId);
+      Artifact treeArtifact = mgr.getTreeArtifact(treeId);
       
-      if (treeName == null)
+      if (treeArtifact == null)
       {
          this.ast = (Aast) value;
       }
       else
       {
-         File file = new File(treeName);
-         if (!(file.exists() && file.isFile()))
+         if (!treeArtifact.exists())
          {
             this.ast = (Aast) value;
          }
@@ -186,7 +189,7 @@
          {
             AstManager mgr = AstManager.get();
             long treeId = mgr.getTreeId(astId);
-            Aast tree = mgr.loadTree(mgr.getTreeName(treeId));
+            Aast tree = mgr.loadTree(mgr.getTreeArtifact(treeId));
             // keep a copy of this tree registered
             resolver.registerTree(tree.duplicate());
             

=== modified file 'src/com/goldencode/p2j/convert/db/ConversionData.java'
--- old/src/com/goldencode/p2j/convert/db/ConversionData.java	2023-01-10 09:20:39 +0000
+++ new/src/com/goldencode/p2j/convert/db/ConversionData.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : ConversionData.java
 ** Abstract : Main access to incremental conversion db-backed data.
 **
-** Copyright (c) 2020-2022, Golden Code Development Corporation.
+** Copyright (c) 2020-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description----------------------------------
 ** 001 CA  20200412 Created initial version, for incremental conversion support.
@@ -16,6 +16,9 @@
 **                  operation.
 **     GES 20220502 Match API method name change.
 **     RAA 20230109 Changed inline statement(s) to prepared statement(s).
+** 003 DDF 20250408 Use the artifact to retrieve the filename, otherwise use getName().
+**     DDF 20250414 Make use of artifacts instead of the filename.
+**     DDF 20250424 Renamed getTreeName() to getTreeArtifact().
 */
 
 /*
@@ -79,6 +82,8 @@
 import java.util.*;
 import java.util.function.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.preproc.*;
 import com.goldencode.p2j.uast.*;
@@ -128,7 +133,8 @@
       
       for (int i = 0; i < exts.length; i++)
       {
-         long astId = mgr.getTreeId(file + exts[i]);
+         Artifact artifact = ArtifactManager.addArtifact(file + exts[i]);
+         long astId = mgr.getTreeId(artifact);
          if (astId != AstManager.INVALID_ID)
          {
             fileIds[i] = astId;
@@ -220,16 +226,16 @@
       Set<String> depFiles = new HashSet<>();
       dependencies.forEach((fileId) -> 
       {
-         String name = mgr.getTreeName(fileId);
+         Artifact treeArtifact = mgr.getTreeArtifact(fileId);
          for (String ext : exts)
          {
-            if (!ext.isEmpty() && name.endsWith(ext))
+            if (!ext.isEmpty() && treeArtifact.endsWith(ext))
             {
-               name = name.substring(0, name.length() - ext.length());
+               treeArtifact = ArtifactManager.getOriginalArtifact(treeArtifact, ext);
                break;
             }
          }
-         depFiles.add(name);
+         depFiles.add(treeArtifact.getRelativePath());
       });
       
       return depFiles;
@@ -702,7 +708,15 @@
    {
       connect();
       
-      String filename = clsDef.getFilename();
+      String filename;
+      if (clsDef.getArtifact() != null)
+      {
+         filename = clsDef.getArtifact().getRelativePath();
+      }
+      else
+      {
+         filename = clsDef.getName();
+      }
       String qname = clsDef.getName();
       String jpkgname = clsDef.getJavaPackage();
       String jclassname = clsDef.getSimpleJavaName();
@@ -831,18 +845,18 @@
     * 
     * @param    name
     *           The collection name.
-    * @param    file
-    *           The AST file which has finished processing.
+    * @param    artifact
+    *           The AST artifact which has finished processing.
     */
-   public static void compact(String name, String file)
+   public static void compact(String name, Artifact artifact)
    {
       StoredCollection c = helper.getCollection(name);
       if (c != null)
       {
          AstManager mgr = AstManager.get();
-         long fileId = mgr.getTreeId(file);
+         long artifactAstId = mgr.getTreeId(artifact);
          
-         ((StoredCollection) c).compact(fileId);
+         ((StoredCollection) c).compact(artifactAstId);
       }
    }
    

=== modified file 'src/com/goldencode/p2j/pattern/AstSymbolResolver.java'
--- old/src/com/goldencode/p2j/pattern/AstSymbolResolver.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/pattern/AstSymbolResolver.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : AstSymbolResolver.java
 ** Abstract : Symbol resolver which is aware of AST-related issues
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
 ** 001 ECF 20050301   @20135 Created initial version. Implementation of a
@@ -121,7 +121,10 @@
 ** 035 CA  20200412          Added incremental conversion support.
 ** 036 CA  20201015          Replaced java.util.Stack with ArrayDeque (as synchronization is not required).
 ** 037 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 038 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250424          Renamed getTreeName() to getTreeArtifact().
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -181,6 +184,8 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.expr.*;
 import com.goldencode.p2j.convert.*;
@@ -349,22 +354,22 @@
     * @param    resolver
     *           The {@link AstSymbolResolver} instance.  If <code>null</code>, use the singleton
     *           from {@link #getResolver()}.
-    * @param    filename
-    *           Name of file in which AST is stored.
+    * @param    artifact
+    *           Artifact in which AST is stored.
     * 
     * @return   Aast read from the file.
     *
     * @throws   IllegalArgumentException
-    *           if there is any error loading <code>filename</code>.
+    *           if there is any error loading <code>artifact</code>.
     */
-   public static Aast loadTree(AstSymbolResolver resolver, String filename)
+   public static Aast loadTree(AstSymbolResolver resolver, Artifact artifact)
    {
       if (resolver == null)
       {
          resolver = getResolver();
       }
 
-      final Aast ast = AstManager.get().loadTree(filename);
+      final Aast ast = AstManager.get().loadTree(artifact);
       resolver.registerTree(ast);
       
       return ast;
@@ -407,17 +412,17 @@
       
       // the AST was not found among those already loaded and registered; load the enclosing
       // tree and try again
-      String filename = AstManager.get().getTreeName(id);
-      if (!(filename.endsWith(".ast")    ||
-            filename.endsWith(".dict")   || 
-            filename.endsWith(".jast")   ||
-            filename.endsWith(".schema") ||
-            filename.endsWith(".p2o")))
+      Artifact artifact = AstManager.get().getTreeArtifact(id);
+      if (!(artifact.endsWith(".ast")    ||
+            artifact.endsWith(".dict")   ||
+            artifact.endsWith(".jast")   ||
+            artifact.endsWith(".schema") ||
+            artifact.endsWith(".p2o")))
       {
-         filename = String.format("%s.ast", filename);
+         artifact = ArtifactManager.addArtifact(artifact.getRelativePath() + ".ast");
       }
 
-      loadTree(resolver, filename);
+      loadTree(resolver, artifact);
       ast = getAst(resolver, id, false);
       
       return ast;

=== modified file 'src/com/goldencode/p2j/pattern/CommonAstSupport.java'
--- old/src/com/goldencode/p2j/pattern/CommonAstSupport.java	2024-04-25 20:55:17 +0000
+++ new/src/com/goldencode/p2j/pattern/CommonAstSupport.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : CommonAstSupport.java
 ** Abstract : Common symbol resolution and function support for AST processing
 **
-** Copyright (c) 2005-2024, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 ECF 20050228   @20100 Created initial version. Exposes basic AST
@@ -293,6 +293,10 @@
 ** 104 CA  20240324          Performance improvement for runtime annotations: are stored using an integer
 **                           instead of string.
 ** 105 OM  20240416          Extracted UserGeneratedException in its own source file.
+** 106 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250423          Added getArtifact().
+**     DDF 20250424          Removed getFile(), the artifact will be used instead.
+**     DDF 20250430          Temporarily added back getPersistenceFilename().
 */
 
 /*
@@ -356,6 +360,9 @@
 import java.util.*;
 import java.util.concurrent.atomic.*;
 import java.util.logging.*;
+
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.expr.*;
 import com.goldencode.p2j.convert.*;
@@ -1474,37 +1481,37 @@
       }
       
       /**
-       * Load the AST stored in the given file and return it. Do not register it with the AST
+       * Load the AST stored in the given artifact and return it. Do not register it with the AST
        * resolver
        * 
-       * @param   filename
-       *          Name of file in which AST is stored.
+       * @param   artifact
+       *          Artifact in which AST is stored.
        * 
-       * @return  Aast read from the file.
+       * @return  Aast read from the artifact.
        * 
        * @throws  IllegalArgumentException
-       *          if there is any error loading <code>filename</code>.
+       *          if there is any error loading <code>artifact</code>.
        */
-      public Aast parseTree(String filename)
+      public Aast parseTree(Artifact artifact)
       {
-         return AstManager.get().loadTree(filename);
+         return AstManager.get().loadTree(artifact);
       }
       
       /**
-       * Load the AST stored in the given file. This will register the AST and all of its
+       * Load the AST stored in the artifact. This will register the AST and all of its
        * descendants with the current AST resolver.
        * 
-       * @param   filename
-       *          Name of file in which AST is stored.
+       * @param   artifact
+       *          Artifact in which AST is stored.
        * 
-       * @return  Aast read from the file.
+       * @return  Aast read from the artifact.
        *
        * @throws  IllegalArgumentException
-       *          if there is any error loading <code>filename</code>.
+       *          if there is any error loading <code>artifact</code>.
        */
-      public Aast loadTree(String filename)
+      public Aast loadTree(Artifact artifact)
       {
-         return AstSymbolResolver.loadTree(getResolver(), filename);
+         return AstSymbolResolver.loadTree(getResolver(), artifact);
       }
       
       /**
@@ -1570,26 +1577,26 @@
       }
       
       /**
-       * Determines if the given file is a valid AST.
-       *
-       * @param    filename
-       *           The filename to check.  It does not need to have the AST extension.
-       *
-       * @return   <code>true</code> if there is an AST for the given file.
+       * Determines if the given artifact is a valid AST.
+       *
+       * @param    artifact
+       *           The artifact to check.  It does not need to have the AST extension.
+       *
+       * @return   <code>true</code> if there is an AST for the given artifact.
        */
-      public boolean isAst(String filename)
+      public boolean isAst(Artifact artifact)
       {
-         if (filename == null || filename.trim().length() == 0)
+         if (artifact == null || artifact.getRelativePath().trim().isEmpty())
          {
             return false;
          }
          
-         if (!filename.endsWith(AstGenerator.AST_POSTFIX))
+         if (!artifact.endsWith(AstGenerator.AST_POSTFIX))
          {
-            filename = filename + AstGenerator.AST_POSTFIX;
+            artifact = ArtifactManager.addArtifact(artifact.getRelativePath() + AstGenerator.AST_POSTFIX);
          }
          
-         return AstManager.get().isExistingAst(filename);
+         return AstManager.get().isExistingAst(artifact);
       }
       
       /**
@@ -1680,19 +1687,6 @@
       }
       
       /**
-       * Get the name of the source code file associated with the current
-       * source AST. The filename will be relative to <code>P2J_HOME</code>.
-       * <p>
-       * Corresponds with runtime variable <code>file</code>.
-       *
-       * @return  Source code filename.
-       */
-      public String getFile()
-      {
-         return Configuration.normalizeFilename(getSource().getFilename());
-      }
-      
-      /**
        * Returns the project relative filename of the copy AST persistence
        * file, using the default persistence naming convention.
        * <p>
@@ -1703,15 +1697,24 @@
        */
       public String getPersistenceFilename()
       {
-         String name = getCopy().getFilename();
-         
+         String name = getCopy().getArtifact().getRelativePath();
+      
          if (name == null)
+         {
             return null;
-         
-         StringBuilder buf = new StringBuilder(name);
-         buf.append(AstGenerator.AST_POSTFIX);
-         
-         return buf.toString();
+         }
+      
+         return name + AstGenerator.AST_POSTFIX;
+      }
+      
+      /**
+       * Get the artifact associated with the current source AST.
+       *
+       * @return  Source code artifact.
+       */
+      public Artifact getArtifact()
+      {
+         return getSource().getArtifact();
       }
       
       /**
@@ -3513,9 +3516,9 @@
       {
          Aast root = getCopy().getAncestor(-1);
          
-         StringBuilder buf = new StringBuilder(root.getFilename());
+         StringBuilder buf = new StringBuilder(root.getArtifact().getRelativePath());
          buf.append(AstGenerator.AST_POSTFIX);
-         String fname = Configuration.normalizeFilename(buf.toString());
+         Artifact fname = ArtifactManager.addArtifact(buf.toString());
          
          AstManager.get().saveTree(root, fname, false);
          
@@ -3527,11 +3530,11 @@
        * convention. This will overwrite any existing file of the same name
        * without complaint or warning.
        *
-       * @param   filename
-       *          The file to persist.
+       * @param   artifact
+       *          The artifact to persist.
        * @param   projectRelative
-       *          <code>true</code> if the filename is relative to the
-       *          project home directory, <code>false</code> if the filename 
+       *          <code>true</code> if the artifact is relative to the
+       *          project home directory, <code>false</code> if the artifact
        *          is absolute or relative to the current directory.
        *
        * @return  <code>true</code>.
@@ -3542,11 +3545,11 @@
        * @throws  AstException
        *          if any error occurs persisting the AST.
        */
-      public boolean persist(String filename, boolean projectRelative)
+      public boolean persist(Artifact artifact, boolean projectRelative)
       throws ConfigurationException,
              AstException
       {
-         return persist(filename, projectRelative, false);
+         return persist(artifact, projectRelative, false);
       }
       
       /**
@@ -3554,11 +3557,11 @@
        * convention. This will overwrite any existing file of the same name
        * without complaint or warning.
        *
-       * @param   filename
-       *          The file to persist.
+       * @param   artifact
+       *          The artifact to persist.
        * @param   projectRelative
-       *          <code>true</code> if the filename is relative to the
-       *          project home directory, <code>false</code> if the filename 
+       *          <code>true</code> if the artifact is relative to the
+       *          project home directory, <code>false</code> if the artifact
        *          is absolute or relative to the current directory.
        * @param   terse
        *          <code>true</code> to persist the AST in terse mode,
@@ -3572,7 +3575,7 @@
        * @throws  AstException
        *          if any error occurs persisting the AST.
        */
-      public boolean persist(String  filename,
+      public boolean persist(Artifact artifact,
                              boolean projectRelative,
                              boolean terse)
       throws ConfigurationException,
@@ -3580,7 +3583,7 @@
       {
          Aast root = getCopy().getAncestor(-1);
 
-         return persist(root, filename, projectRelative, terse);
+         return persist(root, artifact, projectRelative, terse);
       }
 
       
@@ -3591,11 +3594,11 @@
        *
        * @param   root
        *          The root AST.
-       * @param   filename
-       *          The file to persist.
+       * @param   artifact
+       *          The artifact to persist.
        * @param   projectRelative
-       *          <code>true</code> if the filename is relative to the
-       *          project home directory, <code>false</code> if the filename 
+       *          <code>true</code> if the artifact is relative to the
+       *          project home directory, <code>false</code> if the artifact
        *          is absolute or relative to the current directory.
        * @param   terse
        *          <code>true</code> to persist the AST in terse mode,
@@ -3609,32 +3612,27 @@
        * @throws  AstException
        *          if any error occurs persisting the AST.
        */
-      public boolean persist(Aast    root,
-                             String  filename,
-                             boolean projectRelative,
-                             boolean terse)
+      public boolean persist(Aast     root,
+                             Artifact artifact,
+                             boolean  projectRelative,
+                             boolean  terse)
       throws ConfigurationException,
              AstException
       {
-         if (projectRelative)
-         {
-            filename = Configuration.normalizeFilename(filename);
-         }
-         
-         AstManager.get().saveTree(root, filename, terse);
+         AstManager.get().saveTree(root, artifact, terse);
          
          return true;
       }
       
       /**
-       * Add the given AST as a new program file, by registering its filename.
+       * Add the given AST as a new program file, by registering its artifact.
        * 
        * @param    root
        *           The AST root.
        */
       public void addNewProgramFile(Aast root)
       {
-         engine.addNewProgramFile(root.getFilename());
+         engine.addNewProgramFile(root.getArtifact().getRelativePath());
       }
       
       /**

=== modified file 'src/com/goldencode/p2j/pattern/PatternEngine.java'
--- old/src/com/goldencode/p2j/pattern/PatternEngine.java	2023-11-29 15:00:30 +0000
+++ new/src/com/goldencode/p2j/pattern/PatternEngine.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : PatternEngine.java
 ** Abstract : Main driver for the pattern rules framework
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 ECF 20050301   @20131 Created initial version. Provides a command
@@ -223,6 +223,9 @@
 **     SVL 20230108          Improved performance by replacing some "for-each" loops with indexed "for" loops.
 ** 055 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
 ** 056 CA  20231129          Small changes to avoid 'invokeinterface' in the compiled bytecode.
+** 057 GES 20230524          Moved some code to a more generic package.
+**     DDF 20250320          Incorporate usage of artifacts instead of file lists.
+**     DDF 20250414          Make use of artifacts instead of the filename.
 */
 
 /*
@@ -285,12 +288,12 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.util.*;
 import com.goldencode.p2j.uast.*;
-import com.goldencode.p2j.util.*;
 import org.apache.tinkerpop.gremlin.structure.*;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.*;
 
@@ -451,8 +454,8 @@
 public final class PatternEngine
 extends RuleContainer
 {
-   /** List of AST filespecs used to locate persisted AST files. */
-   private final ArrayList<FileList> astSpecs = new ArrayList<>();
+   /** Collection AST artifacts used to locate persisted AST files. */
+   private final ArrayList<ArtifactCollection> astSpecs = new ArrayList<>();
    
    /** List of newly created AST files by the rules processed by this engine. */
    private final ArrayList<String> newProgramFiles = new ArrayList<>();
@@ -493,8 +496,8 @@
    /** Mapping of user variable names to initializer expression strings or values. */
    private Map<String, Object> varInitializers = null;
    
-   /** Ordered set of target paths of files to be processed. */
-   private final LinkedHashSet<String> targetPaths = new LinkedHashSet<>();
+   /** Collection of target artifacts to be processed. */
+   private final ArtifactCollection targetPaths = new ArtifactCollection();
    
    /** Rule currently being (or most recently) processed */
    private Rule activeRule = null;
@@ -806,7 +809,11 @@
       }
       
       File dir = Configuration.toFile(directory);
-      astSpecs.add(new FileSpecList(dir, fileSpec, true));
+      ArtifactCollection artifacts = ArtifactManager.createArtifactsByFileList(FileSpecList.class,
+                                                                               dir,
+                                                                               fileSpec,
+                                                                               true);
+      astSpecs.add(artifacts);
    }
    
    /**
@@ -814,18 +821,18 @@
     * will in turn be applied against a pipeline of rulesets. Any number of
     * such lists may be added.
     *
-    * @param   list
-    *          An existing, non-null list of files.
+    * @param   col
+    *          An existing, non-null collection of artifacts.
     */
-   public void addAstSpec(FileList list)
+   public void addAstSpec(ArtifactCollection col)
    {
       // a null list is meaningless
-      if (list == null)
+      if (col == null)
       {
          return;
       }
       
-      astSpecs.add(list);
+      astSpecs.add(col);
    }
 
    /**
@@ -852,13 +859,13 @@
    
    /**
     * Get an iterator over the ast specifications ({@link
-    * com.goldencode.p2j.util.FileList} objects). Items are iterated in the
+    * com.goldencode.artifacts.ArtifactCollection} objects). Items are iterated in the
     * order in which they were added.
     *
-    * @return  Iterator over AST spec <code>FileList</code> objects used to
-    *          find ASTs to process.
+    * @return  Iterator over AST spec {@code ArtifactCollection} objects used to
+    *          find AST artifacts to process.
     */
-   public Iterator<FileList> astSpecs()
+   public Iterator<ArtifactCollection> astSpecs()
    {
       return astSpecs.iterator();
    }
@@ -1041,16 +1048,17 @@
          Configuration config = Configuration.getInstance();
          
          // Process each stored AST or walk call graph, depending on the mode of operation.
-         Iterator<String> iter = targetPaths.iterator();
+         Iterator<Artifact> iter = targetPaths.iterator();
          while (iter.hasNext())
          {
-            String path = iter.next();
+            Artifact artifact = iter.next();
+            String path = artifact.getAbsolutePath();
             
             config.withFileProfile(path);
             
             if (graph != null)
             {
-               Aast ast = AstManager.get().loadTree(path);
+               Aast ast = AstManager.get().loadTree(artifact);
                long id = ast.getId();
                Vertex node = nodeFilter.acceptNode(id);
                if (node != null)
@@ -1060,7 +1068,7 @@
             }
             else
             {
-               processAst(path);
+               processAst(artifact);
             }
          }
          
@@ -1100,7 +1108,7 @@
             TreeWalkException exc = new TreeWalkException(msg, thr);
             exc.setSourceAstId(srcAst.getId());
             exc.setCopyAstId(resolver.getCopyAst().getId());
-            exc.setFilename(srcAst.getFilename());
+            exc.setFilename(srcAst.getArtifact().getRelativePath());
             
             throw exc;
          }
@@ -1132,7 +1140,7 @@
     * @throws  AstException
     *          if any error occurs loading a persisted AST.
     */
-   public void processFiles(Iterator<File> iter)
+   public void processFiles(Iterator<Artifact> iter)
    throws AstException
    {
       try
@@ -1144,12 +1152,12 @@
          // For each file in the list, process the associated AST.
          while (iter.hasNext())
          {
-            File next = iter.next();
+            Artifact next = iter.next();
             
             // Do not try to process non-existent files.
             if (next.exists())
             {
-               processAst(next.getAbsolutePath());
+               processAst(next);
             }
          }
          
@@ -1427,7 +1435,8 @@
       if (!processedAsts.contains(id) && nodeFilter.acceptNode(node))
       {
          String filename = node.<String>property("os-filename").value();
-         Aast ast = AstManager.get().loadTree(filename + ".ast");
+         Artifact artifact = ArtifactManager.addArtifact(filename + ".ast");
+         Aast ast = AstManager.get().loadTree(artifact);
          processAst(ast);
          processedAsts.add(id);
       }
@@ -1493,8 +1502,8 @@
     * ruleset and passed on to the next in the pipeline for further
     * processing.
     *
-    * @param   astName
-    *          Name of the AST persisted file to be processed.
+    * @param   astArtifact
+    *          Artifact of the AST persisted file to be processed.
     *
     * @return  Processed copy of AST.
     *
@@ -1503,11 +1512,11 @@
     *
     * @see     #apply
     */
-   private Aast processAst(String astName)
+   private Aast processAst(Artifact astArtifact)
    throws AstException
    {
       // Get the original source AST from persistence.
-      Aast ast = AstManager.get().loadTree(astName);
+      Aast ast = AstManager.get().loadTree(astArtifact);
       return processAst(ast);
    }
    
@@ -1547,8 +1556,8 @@
       // report filename being processed if quiet mode is off
       if (getDebugLevel() >= MSG_STATUS)
       {
-         String filename = ast.getFilename();
-         if (filename == null)
+         Artifact artifact = ast.getArtifact();
+         if (artifact == null)
          {
             // print the descriptive text of the root node if file name is not available
             System.out.printf("%s (%#016x): Unknown file name. %n",
@@ -1557,7 +1566,7 @@
          }
          else
          {
-            System.out.println(filename);
+            System.out.println(artifact.getRelativePath());
          }
       }
       
@@ -1764,46 +1773,20 @@
    throws ConfigurationException
    {
       // consolidate all files derived from AST specs into master set.
-      Iterator<FileList> iter = astSpecs.iterator();
-      if (Configuration.isImportMode())
-      {
-         // assume the path is correct (import only one database at a time) inside jar and just add them
-         // to [targetPaths]
-         while (iter.hasNext())
-         {
-            FileList next = iter.next();
-            if (next instanceof FileSpecList)
-            {
-               FileSpecList fileList = (FileSpecList) next;
-               targetPaths.add(fileList.getStartDir() + "/" + fileList.getFileSpec());
-            }
-            else
-            {
-               File[] files = next.list();
-               for (File file : files)
-               {
-                  targetPaths.add(file.getName());
-               }
-            }
-         }
-      }
-      else
-      {
-         while (iter.hasNext())
-         {
-            FileList fileList = iter.next();
-            File[] files = fileList.list();
-            int len = files.length;
-            for (int i = 0; i < len; i++)
-            {
-               targetPaths.add(toNormalizedPath(files[i].getAbsolutePath()));
-            }
-         }
-         
-         for (int i = 0; i < targetNames.size(); i++)
-         {
-            String name = targetNames.get(i);
-            targetPaths.add(toNormalizedPath(new File(name).getAbsolutePath()));
+      Iterator<ArtifactCollection> iter = astSpecs.iterator();
+      // assume the path is correct (import only one database at a time) inside jar and just add them
+      // to [targetPaths]
+      while (iter.hasNext())
+      {
+         ArtifactCollection next = iter.next();
+         Iterator<Artifact> artifactIter = next.iterator();
+         while (artifactIter.hasNext())
+         {
+            Artifact artifact = artifactIter.next();
+            if (artifact.exists())
+            {
+               targetPaths.add(artifact);
+            }
          }
       }
       
@@ -1819,29 +1802,13 @@
             iter = astSpecs.iterator();
             while (iter.hasNext())
             {
-               FileList fileList = iter.next();
-               
-               if (fileList instanceof FileSpecList)
-               {
-                  FileSpecList fsl = (FileSpecList) fileList;
-                  String dir = fsl.getStartDir().getAbsolutePath();
-                  
-                  buf.append("   Starting Directory:  ")
-                     .append(Configuration.normalizeFilename(dir))
-                     .append(";  RegEx File Specification:  ")
-                     .append(fsl.getFileSpecAsRegEx());
-               }
-               else
-               {
-                  String[] list = fileList.listFilenames();
-                  
-                  StringHelper.appendOnNewLine(buf, "   List:");
-                  
-                  for (int j = 0; j < list.length; j++)
-                  {
-                     buf.append("      ")
-                        .append(Configuration.normalizeFilename(list[j]));
-                  }
+               ArtifactCollection col = iter.next();
+               List<String> list = ArtifactManager.getArtifactsRelativePath(col);
+               StringHelper.appendOnNewLine(buf, "   List:");
+               Iterator<String> fileIter = list.iterator();
+               while (fileIter.hasNext())
+               {
+                  buf.append("      ").append(fileIter.next());
                }
                StringHelper.appendOnNewLine(buf, "");
             }
@@ -2206,7 +2173,9 @@
          
          if (explicit)
          {
-            engine.addAstSpec(new ExplicitFileList(filelist));
+            ArtifactCollection artifacts = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                                     (Object) filelist);
+            engine.addAstSpec(artifacts);
          }
          
          if (callGraphMode)

=== modified file 'src/com/goldencode/p2j/pattern/SearchTrees.java'
--- old/src/com/goldencode/p2j/pattern/SearchTrees.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/pattern/SearchTrees.java	2025-05-09 07:59:16 +0000
@@ -3,17 +3,19 @@
 ** Abstract : drives the pattern engine service to search for matches to
 **            a given expression
 **
-** Copyright (c) 2007-2023, Golden Code Development Corporation.
+** Copyright (c) 2007-2025, Golden Code Development Corporation.
 **
-** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
-** 001 GES 20070511 NEW   @33479 Created initial version that provides
-**                               support for command line processing based
-**                               on given expression. This is an alternate,
-**                               special-purpose driver for the pattern
-**                               engine.
-** 002 GES 20070514 CHG   @33496 Moved debug levels into an interface.
-** 003 TJD 20220504              Upgrade do Java 11 minor changes
-** 004 GBB 20230512              Logging methods replaced by CentralLogger/ConversionStatus.
+** -#- -I- --Date-- --JPRM-- -----------------------------------Description------------------------------------
+** 001 GES 20070511   @33479 Created initial version that provides
+**                           support for command line processing based
+**                           on given expression. This is an alternate,
+**                           special-purpose driver for the pattern
+**                           engine.
+** 002 GES 20070514   @33496 Moved debug levels into an interface.
+** 003 TJD 20220504          Upgrade do Java 11 minor changes
+** 004 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 005 GES 20230524          Moved some code to a more generic package.
+**     DDF 20250320          Replaced usage of FileList with ArtifactCollection.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -74,8 +76,8 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.p2j.convert.*;
-import com.goldencode.p2j.util.*;
 import com.goldencode.util.*;
 
 /**
@@ -242,7 +244,7 @@
       
       idx++;
       
-      FileList files = null;
+      ArtifactCollection artifacts = null;
       
       // at this point idx points to the next arg after the options, this
       // must be one of our various forms of filename input
@@ -262,7 +264,7 @@
             filelist[j - idx] = args[j];
          }
          
-         files = new ExplicitFileList(filelist);
+         artifacts = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class, (Object) filelist);
       }
       else
       {
@@ -272,16 +274,17 @@
             syntax("Missing/incomplete filespec parameter.", -7);
          }
          
-         files = new FileSpecList(new File(args[idx]),
-                                  args[idx+1],
-                                  recursion);
+         artifacts = ArtifactManager.createArtifactsByFileList(FileSpecList.class,
+                                                               new File(args[idx]),
+                                                               args[idx + 1],
+                                                               recursion);
       }
       
       try
       {
          // create a new pattern engine, configure it and run
          PatternEngine engine = new PatternEngine();
-         engine.addAstSpec(files);
+         engine.addAstSpec(artifacts);
          PatternEngine.setDebugLevel(debug);
          engine.setReadOnly(true);
          engine.setVariableInitializers(initializers);

=== modified file 'src/com/goldencode/p2j/pattern/ServiceSupport.java'
--- old/src/com/goldencode/p2j/pattern/ServiceSupport.java	2024-03-19 08:55:31 +0000
+++ new/src/com/goldencode/p2j/pattern/ServiceSupport.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : ServiceSupport.java
 ** Abstract : Helper code and APIs to identify legacy services (REST, SOAP, WEB) from OpenEdge artifacts.
 **
-** Copyright (c) 2019-2024, Golden Code Development Corporation.
+** Copyright (c) 2019-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------------Description---------------------------------------
 ** 001 CA  20190611 First version.
@@ -36,6 +36,10 @@
 ** 011 CA  20240229 Fixed case when the ProcPath starts with './' or is empty.
 **     CA  20240404 Address '..' by normalizing the path; any '..' at the beginning is not supported.
 ** 012 CA  20240319 Fixed CA/011 to add a separator between the path and filename.
+** 013 GES 20230524 Moved some code to a more generic package.
+**     DDF 20250320 Replaced usage of FileList with ArtifactCollection.
+**     DDF 20250327 Finalized replacement of FileSpecList.
+**     DDF 20250414 Make use of artifacts instead of the filename.
 */
 
 /*
@@ -93,6 +97,7 @@
 
 package com.goldencode.p2j.pattern;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.expr.*;
 import com.goldencode.p2j.cfg.*;
@@ -204,7 +209,7 @@
    }
    
    /**
-    * Get the list of JAST files which were generated for legay open client proxy programs.
+    * Get the collection of JAST artifacts which were generated for legay open client proxy programs.
     * 
     * @param    doneAppObjects
     *           The set of (Sub)AppObject class names (qualified with their packages), which can have their 
@@ -212,7 +217,7 @@
     *           
     * @return   See above.
     */
-   public static FileList getProxyClientPrograms(Set<String> doneAppObjects)
+   public static ArtifactCollection getProxyClientPrograms(Set<String> doneAppObjects)
    {
       WorkArea wa = context.get();
 
@@ -225,12 +230,15 @@
       
       for (Aast ast : wa.appObjectJast.values())
       {
-         filenames.add(ast.getFilename());
+         filenames.add(ast.getArtifact().getRelativePath());
       }
       
       wa.appObjectJast.keySet().removeAll(doneAppObjects);
-      
-      return new ExplicitFileList(filenames.toArray(new String[0]));
+
+      String[] fileArray = filenames.toArray(new String[0]);
+      ArtifactCollection artifacts = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                               (Object) fileArray);
+      return artifacts;
    }
    
    /**
@@ -375,16 +383,18 @@
          }
          else
          {
-            File[] files = null;
-            
             // must be a directory
             if (input.isDirectory())
             {
-               FileSpecList list = new FileSpecList(input, "*.xpxg", true);
-               files = list.list();
-               for (File xpxg : files)
+               ArtifactCollection col = ArtifactManager.createArtifactsByFileList(FileSpecList.class,
+                                                                                  input,
+                                                                                  "*.xpxg",
+                                                                                  true);
+               Iterator<Artifact> iter = col.iterator();
+               while (iter.hasNext())
                {
-                  res.addAll(parseProxyProgram(xpxg));
+                  Artifact a = iter.next();
+                  res.addAll(parseProxyProgram(a.getFile()));
                }
             }
          }

=== modified file 'src/com/goldencode/p2j/persist/ConversionPool.java'
--- old/src/com/goldencode/p2j/persist/ConversionPool.java	2023-07-22 11:23:52 +0000
+++ new/src/com/goldencode/p2j/persist/ConversionPool.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : ConversionPool.java
 ** Abstract : Shared pool of pattern engines for runtime conversion use.
 **
-** Copyright (c) 2014-2023, Golden Code Development Corporation.
+** Copyright (c) 2014-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
 ** 001 ECF 20140405 Created initial version as a performance enhancement.
@@ -20,6 +20,8 @@
 ** 009 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
 ** 010 CA  20230722 Initialize the default-databases at conversion startup (both as a set and individual 
 **                  databases).
+**     DDF 20250414 Make use of artifacts instead of the filename.
+**     DDF 20250423 Replace more filename usages with artifacts.
 */
 
 /*
@@ -79,6 +81,9 @@
 
 import java.util.*;
 import java.util.logging.*;
+
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.directory.*;
@@ -160,17 +165,17 @@
       {
          try
          {
-            String schemaPath = sc.getSchemaResource(schema, ".dict");
-            if (schemaPath == null)
+            Artifact schemaArtifact = sc.getSchemaResource(schema, ".dict");
+            if (schemaArtifact == null)
             {
                throw new ConfigurationException(
                   "The schema '" + schema + "' does not have a .dict file set up in the configuration file.");
             }
             
             // load the schema
-            Aast ast = fileLoader.loadTree(schemaPath);
+            Aast ast = fileLoader.loadTree(schemaArtifact);
             ast.intern();
-            pristineDbAsts.put(schemaPath, ast);
+            pristineDbAsts.put(schemaArtifact.getRelativePath(), ast);
             
             if (!metaActive && schema.equals(metaName))
             {
@@ -178,17 +183,17 @@
             }
             
             // load the .p2o definitions in a similar fashion to .dict, above
-            String p2oPath = sc.getSchemaResource(schema, ".p2o");
-            if (p2oPath == null)
+            Artifact p2oArtifact = sc.getSchemaResource(schema, ".p2o");
+            if (p2oArtifact == null)
             {
                throw new ConfigurationException(
                      "The '" + schema + ".p2o' file containing definitions for '" + schema + 
                      "' schema could not be located by the classloader.");
             }
             
-            ast = fileLoader.loadTree(p2oPath);
+            ast = fileLoader.loadTree(p2oArtifact);
             ast.intern();
-            pristineDbAsts.put(p2oPath, ast);
+            pristineDbAsts.put(p2oArtifact.getRelativePath(), ast);
          }
          catch (AstException | ConfigurationException exc)
          {
@@ -205,24 +210,25 @@
       
       for (String schema : all)
       {
-         String schemaPath = sc.getSchemaResource(schema, ".dict");
+         Artifact schemaArtifact = sc.getSchemaResource(schema, ".dict");
          
          // load the schema
-         if (pristineDbAsts.containsKey(schemaPath))
+         if (pristineDbAsts.containsKey(schemaArtifact.getRelativePath()))
          {
-            Aast schemaAst = pristineDbAsts.get(schemaPath);
+            Aast schemaAst = pristineDbAsts.get(schemaArtifact.getRelativePath());
             schemaAst.intern();
-            AstManager.get().saveTree(schemaAst, schemaPath, false);
+            AstManager.get().saveTree(schemaAst, schemaArtifact, false);
          }
          
          // load the .p2o definitions
-         String p2oPath = schemaPath.replaceAll("\\.dict", ".p2o");
+         String p2oPath = schemaArtifact.getRelativePath().replaceAll("\\.dict", ".p2o");
+         Artifact p2oArtifact = ArtifactManager.addArtifact(p2oPath);
          
          if (pristineDbAsts.containsKey(p2oPath))
          {
             Aast p2oAst = pristineDbAsts.get(p2oPath);
             p2oAst.intern();
-            AstManager.get().saveTree(p2oAst, p2oPath, false);
+            AstManager.get().saveTree(p2oAst, p2oArtifact, false);
          }
       }
       

=== modified file 'src/com/goldencode/p2j/persist/DynamicConversionHelper.java'
--- old/src/com/goldencode/p2j/persist/DynamicConversionHelper.java	2025-01-29 10:22:48 +0000
+++ new/src/com/goldencode/p2j/persist/DynamicConversionHelper.java	2025-05-09 07:59:16 +0000
@@ -49,6 +49,7 @@
 ** 012 AI  20240605 Updated processString to corectly process ' and ".
 ** 013 AS  20240905 Fixed 223 error condition handling.
 ** 014 ICP 20250129 Used int64.of and logical.of to leverage caches instances.
+** 015 DDF 20250414 Make use of artifacts instead of the filename.
 */
 
 /*
@@ -107,6 +108,7 @@
 package com.goldencode.p2j.persist;
 
 import antlr.*;
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -153,16 +155,16 @@
     *           The node's token type.
     * @param    text
     *           The node's text.
-    * @param    p2oFile
-    *           The name of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
+    * @param    p2oArtifact
+    *           The artifact of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
     *           temp-table objects.
     *
     * @return   A {@link DataModelAst} instance setup as noted above.
     */
-   private static DataModelAst createDataModelAst(int type, String text, String p2oFile)
+   private static DataModelAst createDataModelAst(int type, String text, Artifact p2oArtifact)
    {
       AstManager astManager = AstManager.get();
-      long treeId = astManager.getTreeId(p2oFile);
+      long treeId = astManager.getTreeId(p2oArtifact);
       DataModelAst ast = new DataModelAst(new CommonToken(type, text.intern()));
       ast.setId(astManager.getNextNodeId(treeId));
       
@@ -176,21 +178,21 @@
     *           The index component
     * @param    propAst
     *           The associated {@link DataModelAst property AST}.
-    * @param    p2oFile
-    *           The name of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
+    * @param    p2oArtifact
+    *           The artifact of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
     *           temp-table objects.
     *
     * @return   See above.
     */
    private static DataModelAst buildIndexComponent(P2JIndexComponent comp,
                                                    DataModelAst propAst,
-                                                   String p2oFile)
+                                                   Artifact p2oArtifact)
    {
       String name        = comp.getLegacyName();
       String historical  = (String) propAst.getAnnotation(P2OLookup.HISTORICAL);
       String datatype    = (String) propAst.getAnnotation(P2OLookup.DATATYPE);
       
-      DataModelAst root = createDataModelAst(DataModelTokenTypes.INDEX_COL, name.intern(), p2oFile);
+      DataModelAst root = createDataModelAst(DataModelTokenTypes.INDEX_COL, name.intern(), p2oArtifact);
       root.putAnnotation(P2OLookup.HISTORICAL, historical.intern());
       root.putAnnotation(P2OLookup.DATATYPE, datatype.intern());
       if (comp.isIgnoreCase())
@@ -256,18 +258,18 @@
     *
     * @param    schema
     *           The schema name.
-    * @param    p2oFile
-    *           The name of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
+    * @param    p2oArtifact
+    *           The artifact of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
     *           temp-table objects.
     *
     * @return   See above.
     */
-   private static DataModelAst buildRootDataModel(String schema, String p2oFile)
+   private static DataModelAst buildRootDataModel(String schema, Artifact p2oArtifact)
    {
       String pkg = Configuration.getParameter("pkgroot") + ".dmo";
 
       // this is a reverse generation of what schema/p2o.xml does
-      DataModelAst root = createDataModelAst(DataModelTokenTypes.DATA_MODEL, schema, p2oFile);
+      DataModelAst root = createDataModelAst(DataModelTokenTypes.DATA_MODEL, schema, p2oArtifact);
       root.putAnnotation(P2OLookup.PACKAGE, pkg);
 
       return root;
@@ -280,8 +282,8 @@
     *           The buffer instance.
     * @param    rbuf
     *           The {@link RecordBuffer} instance for the buffer.
-    * @param    p2oFile
-    *           The name of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
+    * @param    p2oArtifact
+    *           The artifact of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
     *           temp-table objects.
     *
     * @return   A {@link DataModelAst} buffer definition.
@@ -289,13 +291,13 @@
     * @throws   PersistenceException
     *           In case of problems during preparation of the temp-table schema and p2o ASTs.
     */
-   private static DataModelAst buildDataModelClass(Buffer buf, RecordBuffer rbuf, String p2oFile)
+   private static DataModelAst buildDataModelClass(Buffer buf, RecordBuffer rbuf, Artifact p2oArtifact)
    throws PersistenceException
    {
       String historical = rbuf.getDmoInfo().legacyTable; // trimmed and interned
       String dmoName = rbuf.getDMOName().intern();
       
-      DataModelAst root = createDataModelAst(DataModelTokenTypes.CLASS, dmoName, p2oFile);
+      DataModelAst root = createDataModelAst(DataModelTokenTypes.CLASS, dmoName, p2oArtifact);
       root.putAnnotation(P2OLookup.HISTORICAL, historical);
       
       // build the ID property. Normally, in conversion we use the name configured in p2j.cfg.xml
@@ -303,7 +305,7 @@
       // to runtime directory, the latter will prevail
       DataModelAst idProp = createDataModelAst(DataModelTokenTypes.PRIMARY,
                                                DatabaseManager.PRIMARY_KEY,
-                                               p2oFile);
+                                               p2oArtifact);
       idProp.putAnnotation(P2OLookup.DATATYPE, "Long");
       root.addChild(idProp);
       
@@ -321,7 +323,7 @@
             continue;
          }
          
-         DataModelAst propAst = buildProperty(fieldAnn, p2oFile);
+         DataModelAst propAst = buildProperty(fieldAnn, p2oArtifact);
          
          root.addChild(propAst);
          
@@ -364,7 +366,7 @@
          if (index.getLegacyName() != null)
          {
             // include only legacy indexes
-            root.addChild(buildIndex(buf, rbuf, index, idx, propMap, p2oFile));
+            root.addChild(buildIndex(buf, rbuf, index, idx, propMap, p2oArtifact));
          }
       }
       
@@ -378,18 +380,18 @@
     *
     * @param    fieldAnn
     *           The annotation for the field defining this property at the DMO.
-    * @param    p2oFile
-    *           The name of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
+    * @param    p2oArtifact
+    *           The artifact of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
     *           temp-table objects.
     *
     * @return   A {@link DataModelAst} property definition.
     */
-   private static DataModelAst buildProperty(Property fieldAnn, String p2oFile)
+   private static DataModelAst buildProperty(Property fieldAnn, Artifact p2oArtifact)
    {
       String property = fieldAnn.name;
       String historical = fieldAnn.legacy;
       
-      DataModelAst root = createDataModelAst(DataModelTokenTypes.PROPERTY, property, p2oFile);
+      DataModelAst root = createDataModelAst(DataModelTokenTypes.PROPERTY, property, p2oArtifact);
       root.putAnnotation(P2OLookup.HISTORICAL, historical);
       root.putAnnotation(P2OLookup.DATATYPE, fieldAnn._fwdType.getSimpleName().intern());
       
@@ -423,8 +425,8 @@
     *           The index name.
     * @param    propMap
     *           A map with the {@link DataModelAst property} ASTs by their legacy name.
-    * @param    p2oFile
-    *           The name of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
+    * @param    p2oArtifact
+    *           The artifact of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the
     *           temp-table objects.
     *
     * @return   A {@link DataModelAst index} definition.
@@ -434,9 +436,9 @@
                                           P2JIndex                  index,
                                           String                    idxName,
                                           Map<String, DataModelAst> propMap,
-                                          String                    p2oFile)
+                                          Artifact                  p2oArtifact)
    {
-      DataModelAst root = createDataModelAst(DataModelTokenTypes.INDEX, idxName, p2oFile);
+      DataModelAst root = createDataModelAst(DataModelTokenTypes.INDEX, idxName, p2oArtifact);
       
       root.putAnnotation(P2OLookup.HISTORICAL, index.getLegacyName().intern());
       if (index.isUnique())
@@ -456,7 +458,7 @@
       for (int i = 0; i < comps.size(); i++)
       {
          P2JIndexComponent comp = comps.get(i);
-         root.addChild(buildIndexComponent(comp, propMap.get(comp.getNormalizedName()), p2oFile));
+         root.addChild(buildIndexComponent(comp, propMap.get(comp.getNormalizedName()), p2oArtifact));
       }
       
       return root;
@@ -1203,11 +1205,11 @@
     *          The schema dictionary where the table definitions will be loaded.
     * @param   pcode
     *          Append any new progress code here.
-    * @param   p2oFile
-    *          The name of the {@link DataModelAst} tree with the temp-table objects for the query or
+    * @param   p2oArtifact
+    *          The artifact of the {@link DataModelAst} tree with the temp-table objects for the query or
     *          validation statement.
-    * @param   dictFile
-    *          The name of the {@link ProgressAst} tree with the temp-table objects for conversion
+    * @param   dictArtifact
+    *          The artifact of the {@link ProgressAst} tree with the temp-table objects for conversion
     *          {@link SchemaDictionary}
     * @param   buffers
     *          The buffers used by the query or validation statement.
@@ -1219,8 +1221,8 @@
     */
    static void prepareDynamicTables(SchemaDictionary dict,
                                     StringBuilder pcode,
-                                    String p2oFile,
-                                    String dictFile,
+                                    Artifact p2oArtifact,
+                                    Artifact dictArtifact,
                                     ArrayList<Buffer> buffers,
                                     Map<Buffer, String> tempTableNames)
    throws PersistenceException
@@ -1254,8 +1256,8 @@
             StringBuilder sb;
             if (rootObjs == null)
             {
-               p2oRoot = buildRootDataModel(dbName, p2oFile);
-               p2oRoot.brainwash(p2oFile);
+               p2oRoot = buildRootDataModel(dbName, p2oArtifact);
+               p2oRoot.brainwash(p2oArtifact);
                
                schemaRoot = buildRootSchema(dbName);
                sb = new StringBuilder();
@@ -1285,7 +1287,7 @@
             DataModelAst model = rbuf.getDataModelAst();
             if (model == null)
             {
-               model = buildDataModelClass(impl, rbuf, p2oFile);
+               model = buildDataModelClass(impl, rbuf, p2oArtifact);
                rbuf.setDataModelAst(model);
             }
             else
@@ -1334,10 +1336,10 @@
       // save the P2O tree, to expose it to the conversion rules
       for (Map.Entry<String, Object[]> entry : astRoots.entrySet())
       {
-         AstManager.get().saveTree((DataModelAst) entry.getValue()[0], p2oFile, true);
+         AstManager.get().saveTree((DataModelAst) entry.getValue()[0], p2oArtifact, true);
          
          ProgressAst schemaRoot = (ProgressAst) entry.getValue()[1];
-         AstManager.get().saveTree(schemaRoot, dictFile, true);
+         AstManager.get().saveTree(schemaRoot, dictArtifact, true);
          
          if (schemaRoot.isAnnotation(SchemaDictionary.MUTABLE_ANNOTATION))
          {
@@ -1368,8 +1370,8 @@
     *          The root AST of the converted progress code.
     * @param   buffers
     *          The buffers used by the query or validation statement.
-    * @param   fileName
-    *          The file name of the class containing the converted code.
+    * @param   artifact
+    *          The artifact of the class containing the converted code.
     * @param   javaClassName
     *          The name of the generated java class.
     * @param   localTableName
@@ -1377,7 +1379,7 @@
     */
    static void prepareTree(AnnotatedAst root,
                            ArrayList<Buffer> buffers,
-                           String fileName,
+                           Artifact artifact,
                            String javaClassName,
                            Map<Buffer, String> localTableName)
    {
@@ -1432,6 +1434,7 @@
          root.graftAt(stmt, 0);
       }
       
+      String fileName = artifact.getRelativePath();
       // put annotation related to name, package, etc
       root.putAnnotation("file", fileName);
       root.putAnnotation("classname", javaClassName);

=== modified file 'src/com/goldencode/p2j/persist/DynamicQueryHelper.java'
--- old/src/com/goldencode/p2j/persist/DynamicQueryHelper.java	2025-04-02 14:12:29 +0000
+++ new/src/com/goldencode/p2j/persist/DynamicQueryHelper.java	2025-05-09 07:59:16 +0000
@@ -105,6 +105,7 @@
 ** 058 AI  20250220 Added check for JMX_DEBUG to minimize use of lambda.
 ** 059 RNC 20250318 Do not treat dot or colon as a terminator when inside a comment.
 ** 060 OM  20250320 Avoided NPE in case an error is generated and the current procedure is unknown.
+** 061 DDF 20250414 Make use of artifacts instead of the filename.
 */
 
 /*
@@ -169,6 +170,8 @@
 import java.util.concurrent.atomic.*;
 import java.util.logging.*;
 import antlr.*;
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.cache.*;
 import com.goldencode.p2j.NumberedException;
@@ -394,11 +397,15 @@
          wa.qname        = qname;
          wa.buffers      = allBuffers;
          wa.queryId      = queryId;
-         wa.dynQueryFile = "." + File.separator + "in-mem-p2j-query" + queryId + ".p";
-         wa.jastFile     = wa.dynQueryFile + ".jast";
-         wa.astFile      = wa.dynQueryFile + ".ast";
-         wa.p2oFile      = wa.dynQueryFile + ".p2o";
-         wa.dictFile     = wa.dynQueryFile + ".dict";
+         wa.dynQueryFile = ArtifactManager.addArtifact("." +
+                                                       File.separator +
+                                                       "in-mem-p2j-query" +
+                                                       queryId +
+                                                       ".p");
+         wa.jastFile     = ArtifactManager.addArtifact(wa.dynQueryFile.getRelativePath() + ".jast");
+         wa.astFile      = ArtifactManager.addArtifact(wa.dynQueryFile.getRelativePath() + ".ast");
+         wa.p2oFile      = ArtifactManager.addArtifact(wa.dynQueryFile.getRelativePath() + ".p2o");
+         wa.dictFile     = ArtifactManager.addArtifact(wa.dynQueryFile.getRelativePath() + ".dict");
          
          try
          {
@@ -1878,20 +1885,20 @@
       /** The buffers used by the QUERY resource. */
       private ArrayList<Buffer> buffers = null;
       
-      /** The file name of the class containing the converted query code. */
-      private String dynQueryFile = null;
-      
-      /** The name of the generated {@link JavaAst} tree. */
-      private String jastFile = null;
-      
-      /** The name of the converted {@link ProgressAst} tree. */
-      private String astFile = null;
-      
-      /** The name of the {@link DataModelAst} tree with the temp-table objects for this query. */
-      private String p2oFile = null;
-      
-      /** The name of the {@link ProgressAst} tree with the temp-table objects for schema worker. */
-      private String dictFile = null;
+      /** The artifact of the class containing the converted query code. */
+      private Artifact dynQueryFile = null;
+      
+      /** The artifact of the generated {@link JavaAst} tree. */
+      private Artifact jastFile = null;
+      
+      /** The artifact of the converted {@link ProgressAst} tree. */
+      private Artifact astFile = null;
+      
+      /** The artifact of the {@link DataModelAst} tree with the temp-table objects for this query. */
+      private Artifact p2oFile = null;
+      
+      /** The artifact of the {@link ProgressAst} tree with the temp-table objects for schema worker. */
+      private Artifact dictFile = null;
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/persist/DynamicTablesHelper.java'
--- old/src/com/goldencode/p2j/persist/DynamicTablesHelper.java	2025-02-21 14:46:49 +0000
+++ new/src/com/goldencode/p2j/persist/DynamicTablesHelper.java	2025-05-09 07:59:16 +0000
@@ -109,6 +109,7 @@
 **                  When the update is disabled, the cache file can be now loaded from the classpath.
 **                  Fixed edge case when DMO is found in persistentCacheMap, but classloading fails.
 ** 043 ICP 20250129 Used logical constants to leverage cached instances.
+** 044 DDF 20250414 Make use of artifacts instead of the filename.
 */
 
 /*
@@ -167,6 +168,8 @@
 package com.goldencode.p2j.persist;
 
 import antlr.*;
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.asm.*;
 import com.goldencode.ast.*;
 import com.goldencode.cache.*;
@@ -1415,7 +1418,8 @@
                   String ifacePath = DMO_BASE_PACKAGE.replace('.', File.separatorChar) + File.separator +
                                      DatabaseManager.TEMP_TABLE_SCHEMA + File.separator +
                                      ifaceName + ".java";
-                  AstManager.get().removeTree(ifacePath);
+                  Artifact artifact = ArtifactManager.addArtifact(ifacePath);
+                  AstManager.get().removeTree(artifact);
                } 
             }
          }

=== modified file 'src/com/goldencode/p2j/persist/DynamicValidationHelper.java'
--- old/src/com/goldencode/p2j/persist/DynamicValidationHelper.java	2024-06-13 10:21:17 +0000
+++ new/src/com/goldencode/p2j/persist/DynamicValidationHelper.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : DynamicValidationHelper.java
 ** Abstract : Helper class for dynamic validation expressions.
 **
-** Copyright (c) 2017-2024, Golden Code Development Corporation.
+** Copyright (c) 2017-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
 ** 001 SVL 20171024 Created initial version.
@@ -22,6 +22,7 @@
 ** 007 OM  20240318 API changes in DynamicConversionHelper.
 ** 008 AL2 20240530 Prepare the dictFile as well to be able to work with SchemaWorker in run-time conversion.
 ** 009 AL2 20240613 Cleared dictFile once the dynamic validation is done.
+** 010 DDF 20250414 Make use of artifacts instead of the filename.
 */
 
 /*
@@ -80,6 +81,8 @@
 package com.goldencode.p2j.persist;
 
 import antlr.*;
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.cache.*;
 import com.goldencode.p2j.convert.*;
@@ -192,11 +195,15 @@
          // set the info about what we are currently parsing
          wa.valExpr      = validateExpression;
          wa.buffers      = buffers;
-         wa.dynExprFile = "." + File.separator + "in-mem-valexp" + valExprId + ".p";
-         wa.jastFile     = wa.dynExprFile + ".jast";
-         wa.astFile      = wa.dynExprFile + ".ast";
-         wa.p2oFile      = wa.dynExprFile + ".p2o";
-         wa.dictFile     = wa.dynExprFile + ".dict";
+         wa.dynExprFile = ArtifactManager.addArtifact("." +
+                                                       File.separator +
+                                                       "in-mem-valexp" +
+                                                       valExprId +
+                                                       ".p");
+         wa.jastFile     = ArtifactManager.addArtifact(wa.dynExprFile.getRelativePath() + ".jast");
+         wa.astFile      = ArtifactManager.addArtifact(wa.dynExprFile.getRelativePath() + ".ast");
+         wa.p2oFile      = ArtifactManager.addArtifact(wa.dynExprFile.getRelativePath() + ".p2o");
+         wa.dictFile     = ArtifactManager.addArtifact(wa.dynExprFile.getRelativePath() + ".dict");
          
          try
          {
@@ -560,23 +567,23 @@
       /** The buffers used by the validation expression. */
       private ArrayList<Buffer> buffers = null;
       
-      /** The file name of the class containing the converted validation expression code. */
-      private String dynExprFile = null;
-      
-      /** The name of the generated {@link JavaAst} tree. */
-      private String jastFile = null;
-      
-      /** The name of the converted {@link ProgressAst} tree. */
-      private String astFile = null;
+      /** The artifact of the class containing the converted validation expression code. */
+      private Artifact dynExprFile = null;
+      
+      /** The artifact of the generated {@link JavaAst} tree. */
+      private Artifact jastFile = null;
+      
+      /** The artifact of the converted {@link ProgressAst} tree. */
+      private Artifact astFile = null;
       
       /**
-       * The name of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the temp-table
+       * The artifact of the {@link com.goldencode.p2j.schema.DataModelAst} tree with the temp-table
        * objects for this validation expression.
        */
-      private String p2oFile = null;
+      private Artifact p2oFile = null;
       
-      /** The name of the {@link ProgressAst} tree with the temp-table objects for schema worker. */
-      private String dictFile = null;
+      /** The artifact of the {@link ProgressAst} tree with the temp-table objects for schema worker. */
+      private Artifact dictFile = null;
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/persist/SchemaCheck.java'
--- old/src/com/goldencode/p2j/persist/SchemaCheck.java	2024-05-21 13:58:06 +0000
+++ new/src/com/goldencode/p2j/persist/SchemaCheck.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : SchemaCheck.java
 ** Abstract : Detects schema changes and reflects them in FWD runtime state.
 **
-** Copyright (c) 2022-2024, Golden Code Development Corporation.
+** Copyright (c) 2022-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
 ** 001 ECF 20221001 Created initial version, refactored from BS' changes to DatabaseManager.
@@ -23,6 +23,7 @@
 ** 009 OM  20240507 Added customizations of tables based on the comments/remarks from SQL metadata.
 ** 010 OM  20240516 The SQL tables can be skipped from import if custom comment is set.
 **     OM  20240517 Throw StopConditionException() in the mutable database failed validation.
+** 011 DDF 20250414 Make use of artifacts instead of the filename.
 */
 
 /*
@@ -86,6 +87,9 @@
 import java.util.concurrent.atomic.*;
 import java.util.logging.*;
 import java.util.stream.*;
+
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.asm.*;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.convert.ParmType;
@@ -490,11 +494,16 @@
     * @param   ifaceName
     *          Short name of DMO interface.
     * 
-    * @return  AST registry path name for the given DMO interface.
+    * @return  AST registry artifact for the given DMO interface.
     */
-   private String getAstPath(String ifaceName)
+   private Artifact getAstPath(String ifaceName)
    {
-      return DMO_BASE_PACKAGE.replace('.', '/') + "/" + database.getName() + "/" + ifaceName + ".java";
+      return ArtifactManager.addArtifact(DMO_BASE_PACKAGE.replace('.', '/') +
+                                         "/" +
+                                         database.getName() +
+                                         "/" +
+                                         ifaceName +
+                                         ".java");
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/persist/orm/DDLGeneratorWorker.java'
--- old/src/com/goldencode/p2j/persist/orm/DDLGeneratorWorker.java	2025-02-18 11:48:12 +0000
+++ new/src/com/goldencode/p2j/persist/orm/DDLGeneratorWorker.java	2025-05-09 07:59:16 +0000
@@ -47,6 +47,7 @@
 **                  property value of the component.
 **     DDF 20240209 Add an additional parameter to getComputedColumnString method call, which already
 **                  handles the character type depending on the dialect.
+** 012 DDF 20250414 Make use of artifacts when necessary.
 */
 
 /*
@@ -1104,7 +1105,7 @@
          if (filename == null)
          {
             // if that fails (expected annotation not present), locate the intermediary/artifact file
-            filename = src.getFilename();
+            filename = src.getArtifact().getRelativePath();
          }
          
          if (filename.toLowerCase().endsWith(".p2o"))

=== modified file 'src/com/goldencode/p2j/preproc/Options.java'
--- old/src/com/goldencode/p2j/preproc/Options.java	2025-04-08 13:42:52 +0000
+++ new/src/com/goldencode/p2j/preproc/Options.java	2025-05-09 07:59:16 +0000
@@ -21,6 +21,8 @@
 **     GES 20191010          Added variant of PROCESS-ARCHITECTURE() function for expression
 **                           evaluation.
 ** 011 SV  20250408          Add a default option to propath so no NPE occur. ref 6859
+** 012 GES 20230524          Moved some code to a more generic package.
+**     DDF 20250327          Replaced FileSpecList usage with ArtifactCollection.
 */
 
 /*
@@ -80,6 +82,8 @@
 
 import java.io.File;
 import java.util.*;
+
+import com.goldencode.artifacts.*;
 import com.goldencode.p2j.util.*;
 
 /**
@@ -243,19 +247,18 @@
       {
          File         dir = new File(getBasepath());
          boolean      cs  = isCaseSensitive();
-         FileSpecList fsl = new FileSpecList(dir, "*", true, cs);
-         
-         fallback = fsl.listFilenames();
-         
-         if (!cs)
+         ArtifactCollection col = ArtifactManager.createArtifactsByFileList(FileSpecList.class,
+                                                                            dir,
+                                                                            "*",
+                                                                            true,
+                                                                            cs);
+
+         if (col == null)
          {
-            // the sorting was done case-insensitively, but the names are
-            // still case-sensitive; make them case-neutral
-            for (int i = 0; i < fallback.length; i++)
-            {
-               fallback[i] = fallback[i].toLowerCase();
-            }
+            return null;
          }
+         List<String> paths = ArtifactManager.getArtifactsRelativePath(col);
+         fallback = paths.toArray(new String[0]);
       }
       
       return fallback;

=== modified file 'src/com/goldencode/p2j/preproc/PreprocessorHintsWorker.java'
--- old/src/com/goldencode/p2j/preproc/PreprocessorHintsWorker.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/preproc/PreprocessorHintsWorker.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : PreprocesorHintsWorker.java
 ** Abstract : provides a pattern engine service to lookup preprocessor hints
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------Description-----------------
 ** 001 NVS 20050525   @21280 Created initial version based on UastHints-
@@ -13,7 +13,9 @@
 ** 005 CA  20211214          The AST manager plugin must be context-local, for runtime conversion, as the 
 **                           InMemoryRegistryPlugin is not thread-safe.
 ** 006 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 007 DDF 20250414          Make use of artifacts instead of the filename.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -69,10 +71,9 @@
 
 package com.goldencode.p2j.preproc;
 
-import java.io.*;
-import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -150,9 +151,9 @@
             try
             {
                Aast src  = getResolver().getSourceAst();
-               File file = Configuration.toFile(src.getFilename()); 
+               Artifact artifact = src.getArtifact();
                
-               hints = new PreprocessorHints(file.getAbsolutePath());
+               hints = new PreprocessorHints(artifact.getAbsolutePath());
             }
             
             catch (Exception exc)

=== modified file 'src/com/goldencode/p2j/report/ReportDriver.java'
--- old/src/com/goldencode/p2j/report/ReportDriver.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/report/ReportDriver.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : ReportDriver.java
 ** Abstract : drives the pattern engine based on a list of report definitions
 **
-** Copyright (c) 2007-2023, Golden Code Development Corporation.
+** Copyright (c) 2007-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 GES 20070514   @33498 Created initial version that provides
@@ -36,6 +36,8 @@
 **                           profiles, when a resource (like a file or namespace) is being processed.  Only
 **                           front phase is supported at this time.
 ** 011 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 012 GES 20230524          Moved some code to a more generic package.
+**     DDF 20250320          Replaced usage of FileList with ArtifactCollection.
 */
 
 /*
@@ -93,12 +95,12 @@
 
 package com.goldencode.p2j.report;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.p2j.pattern.*;
 import com.goldencode.p2j.uast.*;
-import com.goldencode.p2j.util.*;
 import com.goldencode.util.*;
 import org.xml.sax.*;
 import javax.xml.parsers.*;
@@ -154,22 +156,22 @@
    /**
     * Run the pattern engine with the given input.
     *
-    * @param    files
-    *           List of ASTs to process.
+    * @param    cols
+    *           Array of collections containing AST artifacts to process.
     * @param    dbg
     *           Debug level.
     * @param    init
     *           Variable initializers to enable.
     */
-   public static void patternEngine(FileList[] files, int dbg, Map<String, Object> init)
+   public static void patternEngine(ArtifactCollection[] cols, int dbg, Map<String, Object> init)
    throws Exception
    {
       PatternEngine engine = new PatternEngine();
-      for (FileList fl : files)
+      for (int i = 0; i < cols.length; i++)
       {
-         if (fl != null)
+         if (cols[i] != null)
          {
-            engine.addAstSpec(fl);
+            engine.addAstSpec(cols[i]);
          }
       }
       PatternEngine.setDebugLevel(dbg);
@@ -486,8 +488,8 @@
       try
       {
          List<ReportDefinition> reports = loadReports(args[opt.idx++]);
-         FileList[] files = new FileList[2];
-         
+         ArtifactCollection[] files = new ArtifactCollection[2];
+
          // at this point idx points to the next arg after the options, this must be one of our various forms
          // of filename input
          
@@ -500,7 +502,11 @@
                syntax("Missing/incomplete filespec parameter.", -7);
             }
             
-            files[0] = new FileSpecList(new File(args[opt.idx]), args[opt.idx + 1], opt.recursion);
+            ArtifactCollection artifacts = ArtifactManager.createArtifactsByFileList(FileSpecList.class,
+                                                                                     new File(args[opt.idx]),
+                                                                                     args[opt.idx + 1],
+                                                                                     opt.recursion);
+            files[0] = artifacts;
             opt.idx += 2;
          }
          
@@ -521,34 +527,10 @@
                String[] filelist = new String[len - opt.idx];
                
                System.arraycopy(args, opt.idx, filelist, 0, len - opt.idx);
-               files[1] = new ExplicitFileList(filelist);
-            }
-         }
-         
-         if (opt.ignore)
-         {
-            // there must be exactly 3 args left
-            int remArgs = args.length - opt.idx;
-            
-            if (remArgs != 3)
-            {
-               syntax("Missing/incomplete filespec + ignore list parameter.", -8);
-            }
-            
-            files[0] = FileListFactory.createBlacklist(args[opt.idx],
-                                                       args[opt.idx + 1],
-                                                       opt.recursion, 
-                                                       false, 
-                                                       args[opt.idx + 2]);
-            
-            if (files[0] == null)
-            {
-               syntax("Failure creating ignore list.", -9);
-            }
-            else
-            {
-               // TODO: this won't work for .dict files
-               files[0] = new ExplicitFileList(files[0].withSuffix(".ast"));
+               ArtifactCollection artifacts =
+                        ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                  (Object) filelist);
+               files[1] = artifacts;
             }
          }
          
@@ -635,8 +617,10 @@
       try
       {
          List<ReportDefinition> reports = loadReports(rptDef);
-         FileList[] fileLists = new FileList[1];
-         fileLists[0] = new ExplicitFileList(files);
+         ArtifactCollection[] fileLists = new ArtifactCollection[1];
+         ArtifactCollection artifactCol = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                                    (Object) files);
+         fileLists[0] = artifactCol;
          Map<String, Object> vars = new HashMap<>();
          
          String appAdminPass = System.getProperty(APP_ADMIN_PASS);

=== modified file 'src/com/goldencode/p2j/report/ReportWorker.java'
--- old/src/com/goldencode/p2j/report/ReportWorker.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/report/ReportWorker.java	2025-05-09 07:59:16 +0000
@@ -3,7 +3,7 @@
 ** Abstract : provides an interface for maintaining sets of statistics from
 **            pattern engine rulesets
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ---------------------------------Description----------------------------------
 ** 001 GES 20050225   @20124 Created initial version supporting functions
@@ -176,6 +176,7 @@
 **                           InMemoryRegistryPlugin is not thread-safe.
 **     RAA 20230109          Changed inline statement(s) to prepared statement(s).
 ** 042 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 043 DDF 20250414          Make use of artifacts instead of the filename.
 */
 
 /*
@@ -241,6 +242,8 @@
 import java.util.logging.*;
 import javax.xml.parsers.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.p2j.convert.*;
 import org.xml.sax.*;
 import com.goldencode.ast.*;
@@ -1493,9 +1496,9 @@
        */
       public void storeSourceFile(Aast root, boolean databaseMode, boolean df)
       {
-         String astFile = root.getFilename();
+         Artifact astArtifact = root.getArtifact();
          
-         if (astFile == null)
+         if (astArtifact == null)
          {
             throw new NullPointerException("Expected non-null file name for AST:" +
                                            System.lineSeparator() +
@@ -1505,10 +1508,10 @@
          {
             Long astId = mgr.getTreeId(root.getId());
             
-            String srcFile = astFile;
+            Artifact srcArtifact = astArtifact;
             if (databaseMode)
             {
-               srcFile = (String) root.getAnnotation(NOTE_SRCFILE);
+               srcArtifact = ArtifactManager.addArtifact((String) root.getAnnotation(NOTE_SRCFILE));
             }
             
             int type = databaseMode ? SRC_SCHEMA : SRC_CODE_CACHE;
@@ -1521,13 +1524,13 @@
             // get a duplicative filter spec for the top-level code directory
             if (!databaseMode || df)
             {
-               String spec = srcFile;
-               int skip = spec.startsWith(REL_PREFIX) ? REL_PREFIX.length() : 0;
-               int pos = spec.indexOf(File.separator, skip);
+               Artifact spec = srcArtifact;
+               int skip = spec.getRelativePath().startsWith(REL_PREFIX) ? REL_PREFIX.length() : 0;
+               int pos = spec.getRelativePath().indexOf(File.separator, skip);
                if (pos >= 0)
                {
-                  spec = spec.substring(0, pos + 1) + "*";
-                  defFilterSpecs.add(spec);
+                  spec = ArtifactManager.addArtifact(spec.getRelativePath().substring(0, pos + 1) + "*");
+                  defFilterSpecs.add(spec.getRelativePath());
                }
             }
             
@@ -1535,11 +1538,11 @@
             
             try
             {
-               Long fid = file2ID.get(srcFile);
+               Long fid = file2ID.get(srcArtifact);
                
                if (fid == null)
                {
-                  stmtFileInsert.setString(1, srcFile);
+                  stmtFileInsert.setString(1, srcArtifact.getRelativePath());
                   stmtFileInsert.execute();
                   stmtFileInsert.clearParameters();
                   
@@ -1548,8 +1551,8 @@
                   rs.next();
                   fid = rs.getLong(1);
                   
-                  file2ID.put(srcFile, fid);
-                  id2File.put(fid, srcFile);
+                  file2ID.put(srcArtifact.getRelativePath(), fid);
+                  id2File.put(fid, srcArtifact.getRelativePath());
                }
                
                stmtAstMapInsert.setLong(1, fid);
@@ -1572,7 +1575,7 @@
                   // this loop stores a marker record for each file with line number 0 and no
                   // text data; this is needed in the event of zero-length source files
                   data = null;
-                  reader = new BufferedReader(new FileReader(srcFile));
+                  reader = new BufferedReader(new FileReader(srcArtifact.getRelativePath()));
                   for (int i = 0; i == 0 || (data = reader.readLine()) != null; i++)
                   {
                      stmtSrcLineInsert.setLong(1, fid);
@@ -1592,7 +1595,9 @@
                   // this loop stores a marker record for each file with line number 0 and no
                   // text data; this is needed in the event of zero-length source files
                   data = null;
-                  reader = new BufferedReader(new FileReader(srcFile + EXT_CACHE));
+                  Artifact readerArtifact = ArtifactManager.addArtifact(srcArtifact.getRelativePath() +
+                                                                   EXT_CACHE);
+                  reader = new BufferedReader(new FileReader(readerArtifact.getRelativePath()));
                   for (int i = 0; i == 0 || (data = reader.readLine()) != null; i++)
                   {
                      stmtSrcLineInsert.setLong(1, fid);
@@ -1939,11 +1944,17 @@
             throw new IllegalArgumentException("No source AST node!");
          }
          
-         String filename = databaseMode
-                           ? (String) src.getAncestor(-1).getAnnotation(NOTE_SRCFILE)
-                           : src.getFilename();
+         Artifact artifact;
+         if (databaseMode)
+         {
+            artifact = ArtifactManager.addArtifact((String) src.getAncestor(-1).getAnnotation(NOTE_SRCFILE));
+         }
+         else
+         {
+            artifact = src.getArtifact();
+         }
          
-         if (filename == null)
+         if (artifact == null)
          {
             throw new AstException("AstRegistry failure for ID " + src.getId().toString() + "!");
          }
@@ -1964,9 +1975,9 @@
             int numDesc    = src.getNumChildren();
             int depth      = src.getDepth();
             
-            sb.append(" <total ").append(Integer.toString(numDesc));
-            sb.append("> <immed ").append(Integer.toString(numImmDesc));
-            sb.append("> <depth ").append(Integer.toString(depth));
+            sb.append(" <total ").append(numDesc);
+            sb.append("> <immed ").append(numImmDesc);
+            sb.append("> <depth ").append(depth);
             sb.append(">");
             text = sb.toString();
          }
@@ -2012,15 +2023,15 @@
          try
          {
             // map filename to ID if not already done (needed for incremental report run)
-            Long fid = file2ID.get(filename);
+            Long fid = file2ID.get(artifact.getRelativePath());
             if (fid == null)
             {
-               stmtFidByFilename.setString(1, filename);
+               stmtFidByFilename.setString(1, artifact.getRelativePath());
                ResultSet rs = stmtFidByFilename.executeQuery();
                if (rs.next())
                {
                   fid = rs.getLong(1);
-                  file2ID.put(filename, fid);
+                  file2ID.put(artifact.getRelativePath(), fid);
                }
                rs.close();
                stmtFidByFilename.clearParameters();

=== modified file 'src/com/goldencode/p2j/report/server/ReportApi.java'
--- old/src/com/goldencode/p2j/report/server/ReportApi.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/report/server/ReportApi.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : ReportApi.java
 ** Abstract : Server API for base reporting services of source code analytics web application.
 **
-** Copyright (c) 2017-2023, Golden Code Development Corporation.
+** Copyright (c) 2017-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -------------------------------------Description---------------------------------------
 ** 001 ECF 20170428 Created first version.
@@ -15,6 +15,10 @@
 **     DDF 20220823 Replaced .nextvalue with next value for.
 **     RAA 20230109 Changed inline statement(s) to prepared statement(s).
 ** 006 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
+** 007 GES 20230524 Moved some code to a more generic package.
+**     DDF 20250320 Replaced usage of FileList with ArtifactCollection.
+**     DDF 20250414 Make use of artifacts instead of the filename.
+**     DDF 20250424 Renamed getTreeName() to getTreeArtifact().
 */
 
 /*
@@ -79,12 +83,12 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.p2j.pattern.*;
 import com.goldencode.p2j.report.*;
-import com.goldencode.p2j.util.*;
 import com.goldencode.util.*;
 
 /**
@@ -1240,15 +1244,17 @@
             // get the AST if we have a (presumably) valid ID
             if (astId > 0)
             {
-               String fileName = astManager.getTreeName(astId);
-               if (fileName != null)
+               Artifact artifact = astManager.getTreeArtifact(astId);
+               if (artifact != null)
                {
                   if (sourceType == SRC_CODE_CACHE)
                   {
                      // code file names are mapped in the registry without an .ast extension
-                     fileName += ".ast";
+                     artifact = ArtifactManager.addArtifact(artifact.getRelativePath() + ".ast");
                   }
-                  Aast ast = astManager.loadTree(DatabaseService.p2jHomePath + fileName);
+                  Artifact homeArtifact = ArtifactManager.addArtifact(DatabaseService.p2jHomePath +
+                                                                      artifact.getRelativePath());
+                  Aast ast = astManager.loadTree(homeArtifact);
                   nodes = prepareAstNodes(ast);
                }
             }
@@ -1630,7 +1636,7 @@
    public void runReport(long reportId)
    {
       List<ReportDefinition> reports = new ArrayList<>();
-      FileList[] files = new FileList[1];
+      ArtifactCollection[] artifactCols = new ArtifactCollection[1];
       int srcType = 0;
       String title;
       boolean databaseMode;
@@ -1675,7 +1681,7 @@
                   throw new RuntimeException("Unexpected report type: " + srcType);
             }
             
-            files[0] = gatherFiles(conn, false, srcType);
+            artifactCols[0] = gatherFiles(conn, false, srcType);
             
             // clear the current results for this report
             ps2.setLong(1, reportId);
@@ -1710,7 +1716,7 @@
       
       try
       {
-         ReportDriver.patternEngine(files, DebugLevels.MSG_STATUS, vars);
+         ReportDriver.patternEngine(artifactCols, DebugLevels.MSG_STATUS, vars);
       }
       catch (Exception exc)
       {
@@ -1763,7 +1769,7 @@
    @WebApi(type = MSG_SEARCH)
    public Object[] runSearch(String criteria, boolean applyFilter, int srcType)
    {
-      FileList files = null;
+      ArtifactCollection artifactCol = null;
       int matchCount = 0;
       int fileCount = 0;
       
@@ -1771,7 +1777,7 @@
       {
          try
          {
-            files = gatherFiles(conn, applyFilter, srcType);
+            artifactCol = gatherFiles(conn, applyFilter, srcType);
             
             // create a new pattern engine, configure it and run simple_search
             Map<String, Object> vars = new HashMap<>();
@@ -1779,7 +1785,7 @@
             vars.put("cmdline", false);
             PatternEngine.setDebugLevel(DebugLevels.MSG_STATUS);
             PatternEngine engine = new PatternEngine();
-            engine.addAstSpec(files);
+            engine.addAstSpec(artifactCol);
             engine.setReadOnly(true);
             engine.setVariableInitializers(vars);
             engine.run("reports/simple_search");
@@ -1939,16 +1945,17 @@
    @WebApi(type = MSG_FLOW_CHART)
    public FlowChartSnippet getFlowChart(long astId, boolean flowDir)
    {
-      String fileName = astManager.getTreeName(astId);
-      if (fileName == null)
+      Artifact artifact = astManager.getTreeArtifact(astId);
+      if (artifact == null)
       {
          return null;
       }
       
       // code file names are mapped in the registry without an .ast extension
-      fileName += ".ast";
-
-      Aast root = astManager.loadTree(DatabaseService.p2jHomePath + fileName);
+      artifact = ArtifactManager.addArtifact(artifact.getRelativePath() + ".ast");
+      Artifact homeArtifact = ArtifactManager.addArtifact(DatabaseService.p2jHomePath +
+                                                          artifact.getRelativePath());
+      Aast root = astManager.loadTree(homeArtifact);
       if (root == null)
       {
          return null;
@@ -2251,12 +2258,12 @@
     * @param   srcType
     *          Type of files (schema or code).
     * 
-    * @return  A file list containing the filenames.
+    * @return  A collection containing artifacts of the filenames.
     * 
     * @throws  SQLException
     *          if there was an error with the database query.
     */
-   private FileList gatherFiles(Connection conn, boolean applyFilter, int srcType)
+   private ArtifactCollection gatherFiles(Connection conn, boolean applyFilter, int srcType)
    throws SQLException
    {
       String suffix;
@@ -2313,8 +2320,11 @@
             name = Configuration.forceHome(name);
             names.add(name);
          }
-         
-         return new ExplicitFileList(names.toArray(new String[names.size()]));
+
+         String[] namesArray = names.toArray(new String[0]);
+         ArtifactCollection artifacts = ArtifactManager.createArtifactsByFileList(ExplicitFileList.class,
+                                                                                  (Object) namesArray);
+         return artifacts;
       }
    }
    

=== modified file 'src/com/goldencode/p2j/schema/DataModelWorker.java'
--- old/src/com/goldencode/p2j/schema/DataModelWorker.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/schema/DataModelWorker.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : DataModelWorker.java
 ** Abstract : Pattern worker with specific knowledge of the data model
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 ECF 20050426   @21002 Created initial version. Modeled after
@@ -81,6 +81,7 @@
 **     OM  20220721          Added [propertyId] annotation for tables with denormalized fields.
 **                           Fixed [PropertyComparator] implementation.
 ** 030 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 031 DDF 20250414          Make use of artifacts instead of the filename.
 */
 
 /*
@@ -135,13 +136,15 @@
 **     Corporation or FWD.  All modified versions must contain the notices of
 **     attribution required in this license.
 */
+
 package com.goldencode.p2j.schema;
 
-import java.io.*;
 import java.util.*;
 import java.util.function.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -271,7 +274,9 @@
    public Aast createRootAst(Aast peerRoot, boolean virtualFile)
    throws ConfigurationException
    {
-      String filename = (virtualFile ? peerRoot.getText() : generateFilename(peerRoot));
+      Artifact filename = virtualFile ?
+                          ArtifactManager.addArtifact(peerRoot.getText()) :
+                          generateArtifact(peerRoot);
       
       return createRootAst(peerRoot, filename, virtualFile);
    }
@@ -283,8 +288,8 @@
     *
     * @param   peerRoot
     *          Root node of Progress schema AST currently being processed.
-    * @param   filename
-    *          The file in which to persist the new AST.
+    * @param   artifact
+    *          The artifact in which to persist the new AST.
     * @param   virtualFile
     *          If <code>true</code> the file is not supposed to be persisted into the file system,
     *          the file name is used to map some ID to the AST
@@ -294,7 +299,7 @@
     * @throws  ConfigurationException
     *          if P2J environment is not properly initialized.
     */
-   public Aast createRootAst(Aast peerRoot, String filename, boolean virtualFile)
+   public Aast createRootAst(Aast peerRoot, Artifact artifact, boolean virtualFile)
    throws ConfigurationException
    {
       WorkArea wa = context.get();
@@ -306,15 +311,7 @@
       // create a new target tree
       wa.root = createAst(DATA_MODEL, "data model", null, null);
       
-      if (virtualFile)
-      {
-         initializeRootAst(wa.root, filename, getResolver());
-      }
-      else
-      {
-         File file = Configuration.toFile(filename);
-         initializeRootAst(wa.root, file, getResolver());
-      }
+      initializeRootAst(wa.root, artifact, getResolver());
       
       getResolver().setTargetRootAst(wa.root);
       
@@ -360,14 +357,13 @@
     * @return   A unique filename based on the source file root name with
     *          ".p2o" appended.
     */
-   private String generateFilename(Aast ast)
+   private Artifact generateArtifact(Aast ast)
    {
-      String filename = ast.getFilename();
-      filename = filename.substring(0, filename.lastIndexOf("."));
+      Artifact artifact = ast.getArtifact();
+      String filename = artifact.getRelativePath().substring(0, artifact.getRelativePath().lastIndexOf("."));
       StringBuilder buf = new StringBuilder(filename);
       buf.append(P2O_POSTFIX);
-      
-      return buf.toString();
+      return ArtifactManager.addArtifact(buf.toString());
    }
    
    /**
@@ -452,8 +448,8 @@
        *
        * @param   peerRoot
        *          Root node of Progress schema AST currently being processed.
-       * @param   filename
-       *          The file in which to persist the new AST.
+       * @param   artifact
+       *          The artifact in which to persist the new AST.
        * @param   virtualFile
        *          If <code>true</code> the file is not supposed to be persisted into the file
        *          system, the file name is used to map some ID to the AST
@@ -463,10 +459,10 @@
        * @throws  ConfigurationException
        *          if P2J environment is not properly initialized.
        */
-      public Aast createRootAst(Aast peerRoot, String filename, boolean virtualFile)
+      public Aast createRootAst(Aast peerRoot, Artifact artifact, boolean virtualFile)
       throws ConfigurationException
       {
-         return DataModelWorker.this.createRootAst(peerRoot, filename, virtualFile);
+         return DataModelWorker.this.createRootAst(peerRoot, artifact, virtualFile);
       }
       
       /**
@@ -885,9 +881,12 @@
       {
          try
          {
-            String file = getSource().getFilename();
-            int pos = file.lastIndexOf('.');
-            Aast root = AstManager.get().loadTree(file.substring(0, pos) + "." + extension);
+            Artifact file = getSource().getArtifact();
+            int pos = file.getRelativePath().lastIndexOf('.');
+            Artifact artifact = ArtifactManager.addArtifact(file.getRelativePath().substring(0, pos) +
+                                                            "." +
+                                                            extension);
+            Aast root = AstManager.get().loadTree(artifact);
             registerTree(root);
          }
          catch (Exception exc)
@@ -910,17 +909,15 @@
       {
          WorkArea wa = context.get();
 
-         String filename = generateFilename(getSource());
+         Artifact artifact = generateArtifact(getSource());
          
          // bypass persistence if there is an empty tree
          if (wa.root.getNumImmediateChildren() == 0)
          {
             try
             {
-               String normal = Configuration.normalizeFilename(filename);
-               
                // no need to maintain this value in the registry
-               AstManager.get().removeTree(normal);
+               AstManager.get().removeTree(artifact);
             }
             catch (Exception exc)
             {
@@ -932,13 +929,11 @@
          
          try
          {
-            filename = Configuration.normalizeFilename(filename);
-            
             // read the existing target tree
-            AstManager.get().saveTree(wa.root, filename, false);
+            AstManager.get().saveTree(wa.root, artifact, false);
             
             // ensure the AST node values remain in the map as their ID, and not the full AST.
-            ConversionData.compact("tmpTabNodes", filename);
+            ConversionData.compact("tmpTabNodes", artifact);
          }
          catch (Exception exc)
          {

=== modified file 'src/com/goldencode/p2j/schema/P2OAccessWorker.java'
--- old/src/com/goldencode/p2j/schema/P2OAccessWorker.java	2025-03-19 13:44:21 +0000
+++ new/src/com/goldencode/p2j/schema/P2OAccessWorker.java	2025-05-09 07:59:16 +0000
@@ -77,7 +77,9 @@
 **     RAA 20240910          checkFieldMatching now uses the legacy name criteria instead of the order by.
 ** 026 OM  20241128          Multi-tenant conversion support: added getTenantType() method.
 ** 027 CA  20250319          This worker can be used before the 'qualified_oo_name' is set in annotation 
-**                           (like in schema conversion phase), so ensure we can load the ClassDefinition. 
+**                           (like in schema conversion phase), so ensure we can load the ClassDefinition.
+** 028 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250430          Avoid NPE when there's no artifact for the visited ast at server startup.
 */
 
 /*
@@ -139,6 +141,7 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.util.*;
@@ -253,8 +256,12 @@
       WorkArea wa = context.get();
       
       ClassDefinition clsDef = SymbolResolver.resolveClassDefinition(ast);
-      String fileName = ast.getFilename();
-      P2OLookup.loadTemporarySchema(fileName, clsDef);
+      Artifact artifact = ast.getArtifact();
+      // There's one call ends up searching for a ./null.p2o file.
+      if (artifact != null)
+      {
+         P2OLookup.loadTemporarySchema(artifact, clsDef);
+      }
       wa.suspiciousQueries.clear();
       wa.indeterminateSortQueries.clear();
    }
@@ -1109,8 +1116,8 @@
     */
    private static class QueryIndexInfo
    {
-      /** The source file in which the query was found. */
-      private final String sourceFile;
+      /** The source artifact in which the query was found. */
+      private final Artifact sourceArtifact;
       
       /** The table against which the query is executed. */
       private final String tableName;
@@ -1142,7 +1149,7 @@
       QueryIndexInfo(Aast ast, String comment, String indexName, int risk)
       throws SchemaException
       {
-         this.sourceFile = ast.getFilename();
+         this.sourceArtifact = ast.getArtifact();
          this.tableName = getTableName(ast);
          this.comment = comment;
          this.indexName = indexName;
@@ -1172,7 +1179,7 @@
       throws IOException,
              SchemaException
       {
-         String cacheFile = sourceFile + ".cache";
+         String cacheFile = sourceArtifact.getRelativePath() + ".cache";
          String sep = System.lineSeparator();
          String dsep = sep + sep;
          String pad = "   ";
@@ -1215,7 +1222,7 @@
       throws IOException,
              SchemaException
       {
-         String cacheFile = sourceFile + ".cache";
+         String cacheFile = sourceArtifact.getRelativePath() + ".cache";
          String sep = System.lineSeparator();
          
          try (BufferedReader br = new BufferedReader(new FileReader(cacheFile)))

=== modified file 'src/com/goldencode/p2j/schema/P2OLookup.java'
--- old/src/com/goldencode/p2j/schema/P2OLookup.java	2025-02-18 11:48:12 +0000
+++ new/src/com/goldencode/p2j/schema/P2OLookup.java	2025-05-09 07:59:16 +0000
@@ -109,6 +109,10 @@
 **     RAA 20240906          Added writeHeaderToIndeterminateSortFile() method.
 ** 046 OM  20241128          Multi-tenant conversion support: added getTenantType() method.
 ** 047 DDF 20240205          Added NON_NULLABLE annotation to index component.
+** 048 DDF 20250320          Create artifacts for .p2o files.
+**     DDF 20250408          Create the artifact for a missed .p2o scenario.
+**     DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250423          Replace more usages of filename with artifacts.
 */
 
 /*
@@ -171,6 +175,8 @@
 import java.util.function.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.util.*;
@@ -401,13 +407,13 @@
    
    /**
     * Attempts to locate the {@code P2OLookup} for an existing dynamic or temporary schema. The AST tree of
-    * {@code filename} is loaded and the {@code dynamic} flag from its root is analyzed for deciding whether
+    * {@code artifact} is loaded and the {@code dynamic} flag from its root is analyzed for deciding whether
     * it is dynamic or not. In the first case, the existing {@code P20Lookup} instance is returned if one
     * exist. Otherwise, the application defaults to temp-table mode and a new {@code P2OLookup} instance
     * is created and returned.
     * 
-    * @param   filename
-    *          Name of file containing the P2O AST to load.  File may or may not exist.
+    * @param   artifact
+    *          Artifact containing the P2O AST to load. Artifact file may or may not exist.
     * @param   clsDef
     *          The associated class definition for this file, otherwise {@code null}.
     *
@@ -417,12 +423,12 @@
     *          if there is any error loading P2J schema configuration data;
     *          if there is any error loading the AST from persistence.
     */
-   private static P2OLookup dynamicGet(String filename, ClassDefinition clsDef)
+   private static P2OLookup dynamicGet(Artifact artifact, ClassDefinition clsDef)
    throws SchemaException
    {
-      if (AstManager.get().isExistingAst(filename))
+      if (AstManager.get().isExistingAst(artifact))
       {
-         Aast root = AstManager.get().loadTree(filename);
+         Aast root = AstManager.get().loadTree(artifact);
          if (root.isAnnotation(SchemaDictionary.MUTABLE_ANNOTATION))
          {
             P2OLookup dynamicInstance = getInstance(root.getText(), 0);
@@ -433,7 +439,7 @@
             }
          }
       }
-      return new P2OLookup(TEMP_TABLE_KEY, filename, clsDef);
+      return new P2OLookup(TEMP_TABLE_KEY, artifact, clsDef);
    }
    
    /**
@@ -447,8 +453,8 @@
     *
     * @param   schema
     *          Database schema name to which this instance is mapped.
-    * @param   filename
-    *          Name of file containing the P2O AST to load.  File may or may not exist.
+    * @param   artifact
+    *          Artifact containing the P2O AST to load. Artifact file may or may not exist.
     * @param   clsDef
     *          The associated class definition for this file, otherwise <code>null</code>.
     *
@@ -456,14 +462,14 @@
     *          if there is any error loading P2J schema configuration data;
     *          if there is any error loading the AST from persistence.
     */
-   private P2OLookup(String schema, String filename, ClassDefinition clsDef)
+   private P2OLookup(String schema, Artifact artifact, ClassDefinition clsDef)
    throws SchemaException
    {
-      if (AstManager.get().isExistingAst(filename))
+      if (AstManager.get().isExistingAst(artifact))
       {
          try
          {
-            initialize(schema, AstManager.get().loadTree(filename), false);
+            initialize(schema, AstManager.get().loadTree(artifact), false);
          }
          catch (AstException exc)
          {
@@ -491,12 +497,12 @@
             {
                if (exc[0] == null && !cls.isBuiltIn())
                {
-                  String parentFile = cls.getFilename();
+                  String parentFile = cls.getArtifact().getRelativePath();
                   parentFile += P2O_POSTFIX;
-                  parentFile = Configuration.normalizeFilename(parentFile);
+                  Artifact pArtifact = ArtifactManager.addArtifact(parentFile);
                   try
                   {
-                     parr.add(new P2OLookup(schema, parentFile, cls));
+                     parr.add(new P2OLookup(schema, pArtifact, cls));
                   }
                   catch (SchemaException e)
                   {
@@ -544,7 +550,7 @@
    }
    
    /**
-    * Attempt to load the P2O AST stored in <code>filename</code>, which must
+    * Attempt to load the P2O AST stored in <code>artifact</code>, which must
     * represent a temp/work-table schema.  This method should be invoked by
     * conversion service pattern workers which need to load temporary schema
     * information upon loading a new source file AST.  A new instance of this
@@ -553,14 +559,14 @@
     * succession with the same file name, only the first invocation triggers
     * the P2O AST to be loaded.
     * <p>
-    * If no file exists with the name <code>filename</code>, an empty
+    * If no file exists with the <code>artifact/code>, an empty
     * instance of this class is stored in the schema map under the temp table
     * schema key.  This ensures that an out-of-date <code>P2OLookup</code>
     * instance is not maintained under the temp table key beyond it useful
     * life.
     *
-    * @param   filename
-    *          Name of file containing the P2O AST to load.  File may or may
+    * @param   artifact
+    *          Artifact containing the P2O AST to load. Artifact file may or may
     *          not exist, but this value may not be <code>null</code>.
     * @param   clsDef
     *          The associated class definition for this file, otherwise <code>null</code>.
@@ -570,25 +576,23 @@
     *          the given file;  non-existence of the file is not considered
     *          an error.
     */
-   public static void loadTemporarySchema(String filename, ClassDefinition clsDef)
+   public static void loadTemporarySchema(Artifact artifact, ClassDefinition clsDef)
    {
-      filename += P2O_POSTFIX;
-      filename = Configuration.normalizeFilename(filename);
-      
+      Artifact p2oArtifact = ArtifactManager.addArtifact(artifact.getRelativePath() + P2O_POSTFIX);
       WorkArea wa = locate();
       
-      if (!filename.equals(wa.lastTempFilename))
+      if (!p2oArtifact.getRelativePath().equals(wa.lastTempFilename))
       {
          try
          {
-            P2OLookup.dynamicGet(filename, clsDef);
+            P2OLookup.dynamicGet(p2oArtifact, clsDef);
          }
          catch (SchemaException exc)
          {
             throw new RuntimeException(exc);
          }
          
-         wa.lastTempFilename = filename;
+         wa.lastTempFilename = p2oArtifact.getRelativePath();
       }
    }
    
@@ -2229,22 +2233,21 @@
    {
       SchemaConfig config = Configuration.getSchemaConfig();
       boolean runtime = Configuration.isRuntimeConfig();
-      String path;
+      Artifact artifact;
       if (runtime)
       {
-         path = config.getSchemaResource(database, P2O_POSTFIX);
-         if (path == null)
+         artifact = config.getSchemaResource(database, P2O_POSTFIX);
+         if (artifact == null)
          {
             throw new ConfigurationException("Cannot find " + P2O_POSTFIX + " for database " + database);
          }
       }
       else
       {
-         File file = config.getSchemaFile(database, P2O_POSTFIX);
-         path = Configuration.normalizeFilename(file.getAbsolutePath());
+         artifact = config.getSchemaFile(database, P2O_POSTFIX);
       }
       
-      return AstManager.get().loadTree(path);
+      return AstManager.get().loadTree(artifact);
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/schema/SchemaConfig.java'
--- old/src/com/goldencode/p2j/schema/SchemaConfig.java	2025-02-19 11:21:56 +0000
+++ new/src/com/goldencode/p2j/schema/SchemaConfig.java	2025-05-09 07:59:16 +0000
@@ -53,6 +53,8 @@
 ** 020 CA  20230705          For the 'standard' namespace, the 'default database' value must be 'false'.
 ** 021 ICP 20240705          Fixed method of obtaining the class loader.
 ** 022 DDF 20250219          Replaced mandatoryTables() call with filterTables().
+** 023 DDF 20250423          Replace usage of filename with artifacts.
+**     DDF 20250430          Use the resource path when looking for a resource using the class loader.
 */
 
 /*
@@ -113,6 +115,9 @@
 import java.io.*;
 import java.util.*;
 import java.util.logging.*;
+
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.cfg.Parameter;
 import com.goldencode.p2j.classloader.MultiClassLoader;
@@ -505,42 +510,34 @@
    }
    
    /**
-    * Returns a file object representing the import (DF) file associated with the specified database schema
-    * name of the default profile. The file's path is fully qualified.
+    * Returns an artifact object representing the import (DF) file associated with the specified database
+    * schema name of the default profile. The file's path is fully qualified.
     *
     * @param   nsName
     *          Name of desired namespace configuration
     *
-    * @return  Import file.
-    *
-    * @throws  ConfigurationException
-    *          if environment has not been configured correctly.
+    * @return  Import file artifact.
     */
-   public File getImportFile(String nsName)
-   throws ConfigurationException
+   public Artifact getImportFile(String nsName)
    {
       return getImportFile(null, nsName);
    }
    
    /**
-    * Returns a file object representing the import (DF) file associated with the specified database schema
-    * name in a specific profile. The file's path is fully qualified.
+    * Returns an artifact object representing the import (DF) file associated with the specified database
+    * schema name in a specific profile. The file's path is fully qualified.
     *
     * @param   profile
     *          The profile to be processed. If {@code null} the default profile will be used.
     * @param   nsName
     *          Name of desired namespace configuration
     *
-    * @return  Import file.
-    *
-    * @throws  ConfigurationException
-    *          if environment has not been configured correctly.
+    * @return  Import file artifact.
     */
-   public File getImportFile(String profile, String nsName)
-   throws ConfigurationException
+   public Artifact getImportFile(String profile, String nsName)
    {
       NsConfig cfg = getProfile(profile, false).nsConfigs.get(nsName == null ? null : nsName.toLowerCase());
-      return (cfg != null) ? Configuration.toFile(cfg.getImportFile()) : null;
+      return (cfg != null) ? cfg.getImportFile() : null;
    }
    
    /**
@@ -581,28 +578,24 @@
    }
    
    /**
-    * Returns a file object representing the XML document associated with the specified database schema name
-    * of the default profile. The file's path is fully qualified.
+    * Returns an artifact object representing the XML document associated with the specified database schema
+    * name of the default profile. The file's path is fully qualified.
     *
     * @param   nsName
     *          Name of desired namespace configuration
     * @param   type
     *          the type of resource (the file extension, including the dot).
     *
-    * @return  File containing namespace schema XML document.
-    *
-    * @throws  ConfigurationException
-    *          if environment has not been configured correctly.
+    * @return  Artifact containing namespace schema XML document.
     */
-   public File getSchemaFile(String nsName, String type)
-   throws ConfigurationException
+   public Artifact getSchemaFile(String nsName, String type)
    {
       return getSchemaFile(null, nsName, type);
    }
    
    /**
-    * Returns a file object representing the XML document associated with the specified database schema name
-    * of the specific profile. The file's path is fully qualified.
+    * Returns an artifact object representing the XML document associated with the specified database schema
+    * name of the specific profile. The file's path is fully qualified.
     *
     * @param   profile
     *          The profile to be processed. If {@code null} the default profile will be used.
@@ -611,13 +604,9 @@
     * @param   type
     *          the type of resource (the file extension, including the dot).
     *
-    * @return  File containing namespace schema XML document.
-    *
-    * @throws  ConfigurationException
-    *          if environment has not been configured correctly.
+    * @return  Artifact containing namespace schema XML document.
     */
-   public File getSchemaFile(String profile, String nsName, String type)
-   throws ConfigurationException
+   public Artifact getSchemaFile(String profile, String nsName, String type)
    {
       NsConfig cfg = getProfile(profile, false).nsConfigs.get(nsName == null ? null : nsName.toLowerCase());
       if (cfg == null)
@@ -625,11 +614,11 @@
          return null;
       }
       
-      return Configuration.toFile(getSchemaFileName(profile, nsName, type));
+      return getSchemaFileName(profile, nsName, type);
    }
    
    /**
-    * Returns path to the resource representing the XML document associated with the specified
+    * Returns an artifact of the resource representing the XML document associated with the specified
     * database schema name in the default profile.
     *
     * @param   nsName
@@ -637,17 +626,17 @@
     * @param   type
     *          the type of resource (the file extension, including the dot).
     *
-    * @return  path to the resource containing namespace schema XML document or {@code null} if the resource
-    *          doesn't exist.
+    * @return  Artifact of the resource containing namespace schema XML document or {@code null} if the
+    *          resource doesn't exist.
     */
-   public String getSchemaFileName(String nsName, String type)
+   public Artifact getSchemaFileName(String nsName, String type)
    {
       return getSchemaFileName(null, nsName, type);
    }
    
    /**
-    * Returns path to the resource representing the XML document associated with the specified database schema
-    * name in the requested profile.
+    * Returns an artifact of the resource representing the XML document associated with the specified
+    * database schema name in the requested profile.
     *
     * @param   profile
     *          The profile to be processed. If {@code null} the default profile will be used.
@@ -656,15 +645,15 @@
     * @param   type
     *          the type of resource (the file extension, including the dot).
     *
-    * @return  path to the resource containing namespace schema XML document or {@code null} if the resource
-    *          doesn't exist.
+    * @return  Artifact of the resource containing namespace schema XML document or {@code null} if the
+    *          resource doesn't exist.
     */
-   public String getSchemaFileName(String profile, String nsName, String type)
+   public Artifact getSchemaFileName(String profile, String nsName, String type)
    {
       NsConfig cfg = getProfile(profile, false).nsConfigs.get(nsName == null ? null : nsName.toLowerCase());
       if (cfg != null)
       {
-         return cfg.getDataArtifactsLocation() + "/" + nsName + type;
+         return ArtifactManager.addArtifact(cfg.getDataArtifactsLocation() + "/" + nsName + type);
       }
       
       return null;
@@ -679,17 +668,17 @@
     * @param   type
     *          the type of resource (the file extension, including the dot).
     *
-    * @return  path to the resource containing namespace schema XML document or {@code null} if the resource
-    *          doesn't exist.
+    * @return  Artifact of the resource containing namespace schema XML document or {@code null} if the
+    *          resource doesn't exist.
     */
-   public String getSchemaResource(String nsName, String type)
+   public Artifact getSchemaResource(String nsName, String type)
    {
       return getSchemaResource(null, nsName, type);
    }
    
    /**
-    * Returns path to the resource representing the XML document associated with the specified database schema
-    * name in the requested profile.
+    * Returns path to the resource representing the XML document associated with the specified database
+    * schema name in the requested profile.
     *
     * @param   profile
     *          The profile to be processed. If {@code null} the default profile will be used.
@@ -698,16 +687,16 @@
     * @param   type
     *          the type of resource (the file extension, including the dot).
     *
-    * @return  path to the resource containing namespace schema XML document or {@code null} if the resource
-    *          doesn't exist.
+    * @return  Artifact of the resource containing namespace schema XML document or {@code null} if the
+    *          resource doesn't exist.
     */
-   public String getSchemaResource(String profile, String nsName, String type)
+   public Artifact getSchemaResource(String profile, String nsName, String type)
    {
-      String resourceName = getSchemaFileName(profile, nsName, type);
+      Artifact resourceName = getSchemaFileName(profile, nsName, type);
       
       if (resourceName != null)
       {
-         if (MultiClassLoader.getClassLoader().getResource(resourceName) != null)
+         if (MultiClassLoader.getClassLoader().getResource(resourceName.getResourcePath()) != null)
          {
             return resourceName;
          }
@@ -716,17 +705,21 @@
          // while working on IDE, without the need to have a full application jar built
          if (!developerModeWarned)
          {
-            LOG.log(Level.SEVERE,  "Failed to locate '" + resourceName + "' as a resource. Using developer " +
-               "mode: attempting to locate it as local file in ${cvtpath} location.");
+            LOG.log(Level.SEVERE, "Failed to locate '" +
+                                  resourceName.getRelativePath() +
+                                  "' as a resource. Using developer " +
+                                  "mode: attempting to locate it as local file in ${cvtpath} location.");
             developerModeWarned = true;
          }
          
          try
          {
-            File toFile = Configuration.toFile(Configuration.getParameter("cvtpath") + "/" + resourceName);
+            File toFile = Configuration.toFile(Configuration.getParameter("cvtpath") +
+                                               "/" +
+                                               resourceName.getRelativePath());
             if (toFile.exists())
             {
-               return toFile.getCanonicalPath();
+               return ArtifactManager.addArtifact(toFile.getCanonicalPath());
             }
          }
          catch (ConfigurationException | IOException e)
@@ -831,7 +824,7 @@
             sb.append("\n      ");
             sb.append(ns.getName());
             sb.append(":");
-            sb.append("\n        importFile = ").append(ns.getImportFile());
+            sb.append("\n        importFile = ").append(ns.getImportFile().getRelativePath());
             sb.append("\n        *dataArtifacts = ").append(ns.getDataArtifactsLocation());
             sb.append("\n        mutable = ").append(ns.isMutable());
             sb.append("\n        default = ").append(ns.isDefault());
@@ -947,8 +940,8 @@
       /** Whether the schema is mutable (it can be modified externally and is scanned at runtime) */
       private boolean mutable = false;
       
-      /** Name of {@code .df} import file. */
-      private String importFile;
+      /** Artifact of {@code .df} import file. */
+      private Artifact importFile;
       
       /** SQL name conversion override for SQL table and column names. */
       private int conversion = MatchPhraseConstants.SQL_MODE_DEFAULT;
@@ -1017,29 +1010,32 @@
       }
       
       /**
-       * Get DF import filename, defaulting to standard location for {@code .df} files if not explicitly
+       * Get DF import file artifact, defaulting to standard location for {@code .df} files if not explicitly
        * configured in {@code p2j.cfg.xml}.
        *
-       * @return  Import filename.
+       * @return  Import file artifact.
        */
-      public String getImportFile()
+      public Artifact getImportFile()
       {
          if (importFile != null)
          {
-            return Configuration.getInstance().substituteParms(importFile);
+            return importFile;
          }
-         return "data/" + name + ".df";
+         
+         return ArtifactManager.addArtifact("data/" + name + ".df");
       }
       
       /**
        * Set DF import filename.
+       * This method is called when the configuration is created, it requires a file to be parsed so it is
+       * not possible to use an {@code Artifact} as a parameter.
        *
        * @param   importFile
        *          Import filename.
        */
       public void setImportFile(String importFile)
       {
-         this.importFile = importFile;
+         this.importFile = ArtifactManager.addArtifact(importFile);
       }
       
       /**
@@ -1093,7 +1089,8 @@
       public String getDataArtifactsLocation()
       {
          // use the import file (.df) to compute the path relative to project's root
-         String importFile = getImportFile().replace('\\', '/');
+         Artifact importArtifact = getImportFile();
+         String importFile = importArtifact.getRelativePath();
          int i = importFile.lastIndexOf("/");
          
          String path = ""; 

=== modified file 'src/com/goldencode/p2j/schema/SchemaDictionary.java'
--- old/src/com/goldencode/p2j/schema/SchemaDictionary.java	2025-02-17 07:55:49 +0000
+++ new/src/com/goldencode/p2j/schema/SchemaDictionary.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : SchemaDictionary.java
 ** Abstract : schema namespace dictionary
 **
-** Copyright (c) 2004-2024, Golden Code Development Corporation.
+** Copyright (c) 2004-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 ECF 20041216   @19178 Created initial version.
@@ -558,6 +558,7 @@
 **                           the lookup to a specific table, use the normal node search algorithm instead
 ** 109 RNC 20241030          Added null check in getTableProperty(String, int).
 ** 110 CA  20241104          'getFirstDatabase' must check 'bannedSchemas'.
+** 111 DDF 20250414          Make use of artifacts instead of the filename.
 */
 
 /*
@@ -619,6 +620,8 @@
 import java.lang.ref.*;
 import java.util.*;
 import java.util.logging.*;
+
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -3738,16 +3741,16 @@
     * regular schema is persisted separately and shared across all source
     * files.
     *
-    * @param   srcFile
-    *          Name of the 4GL source code file containing the schema
+    * @param   srcArtifact
+    *          Artifact of the 4GL source code file containing the schema
     *          definitions being persisted.
-    * @param   schemaFile
-    *          Name of schema persistence file.
+    * @param   schemaArtifact
+    *          Artifact of schema persistence file.
     *
     * @throws  AstException
     *          if any error occurs writing persistence file.
     */
-   public void persist(String srcFile, String schemaFile)
+   public void persist(Artifact srcArtifact, Artifact schemaArtifact)
    throws AstException
    {
       List<Aast> list = new ArrayList<>();
@@ -3772,15 +3775,13 @@
          return;
       }
       
-      String normal = Configuration.normalizeFilename(schemaFile);
-      
       // Collect all tables under one, unified, database AST. Save the
       // original 4GL source filename in the root node.
       Aast db = new ProgressAst();
       db.setType(DATABASE);
       db.setText(TEMP_TABLE_DB);
-      db.putAnnotation("srcfile", srcFile);
-      db.brainwash(normal);
+      db.putAnnotation("srcfile", srcArtifact.getRelativePath());
+      db.brainwash(schemaArtifact);
       AstManager.get().setFlag(db.getId(), AstGenerator.FLAG_DICT);
       
       // Graft all temp/work tables to the new database AST. IDs are fixed
@@ -3792,7 +3793,7 @@
          Aast nextChild = (Aast) nextDb.getFirstChild();
          while (nextChild != null)
          {
-            if (nextChild.getFilename() == null)
+            if (nextChild.getArtifact() == null)
             {
                // only tables which were added in this parse, avoid the tables loaded from super-classes.
                switch (nextChild.getType())
@@ -3810,7 +3811,7 @@
       
       if (db.getNumImmediateChildren() != 0)
       {
-         AstManager.get().saveTree(db, schemaFile, false);
+         AstManager.get().saveTree(db, schemaArtifact, false);
       }
    }
    
@@ -3847,23 +3848,23 @@
     * primarily to load temp- and work-tables into an instance of the
     * dictionary which already has a primary schema loaded.
     *
-    * @param   schemaFile
-    *          The name of a persisted schema file which is associated with
+    * @param   schemaArtifact
+    *          The artifact of a persisted schema file which is associated with
     *          a Progress source code file.
     *
     * @throws  SchemaException
     *          if any error occurs reading the schema data from persistence.
     */
-   public void loadFromPersistence(String schemaFile)
+   public void loadFromPersistence(Artifact schemaArtifact)
    throws SchemaException
    {
       try
       {
-         loadFromAst(AstManager.get().loadTree(schemaFile), false);
+         loadFromAst(AstManager.get().loadTree(schemaArtifact), false);
       }
       catch (AstException exc)
       {
-         throw new SchemaException("Error reading schema data from " + schemaFile, exc);
+         throw new SchemaException("Error reading schema data from " + schemaArtifact, exc);
       }
    }
    

=== modified file 'src/com/goldencode/p2j/schema/SchemaLoader.java'
--- old/src/com/goldencode/p2j/schema/SchemaLoader.java	2024-04-25 20:55:17 +0000
+++ new/src/com/goldencode/p2j/schema/SchemaLoader.java	2025-05-09 07:59:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : SchemaLoader.java
 ** Abstract : Loads schema namespaces from persistent storage (XML)
 **
-** Copyright (c) 2004-2024, Golden Code Development Corporation.
+** Copyright (c) 2004-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
 ** 001 ECF 20041216   @19183 Created initial version.
@@ -101,6 +101,9 @@
 ** 035 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
 ** 036 OM  20240410          mergeMetadata() copies the _meta tables even if the primary database is empty.
 **                           Skip loading tables from mutable databases, they will be loaded on request.
+** 037 DDF 20250407          Make use of the artifact in the ProgressLexer constructor.
+**     DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250423          config methods return artifacts instead of filenames.
 */
 
 /*
@@ -163,6 +166,9 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
+import com.goldencode.artifacts.SourceArtifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -426,15 +432,17 @@
       try
       {
          // get DF file; if the schema is mutable, it may not exist, and this is valid
-         File file = config.getImportFile(name);
-         boolean useDfFile = file != null && file.exists() && file.isFile();
+         Artifact artifact = config.getImportFile(name);
+         boolean useDfFile = artifact != null && artifact.exists();
          boolean mutable = config.isMutable(name);
          
          if (!quietMode)
          {
             if (useDfFile)
             {
-               System.out.println("Importing '" + file.getName() + "' for schema '" + name + "'...");
+               System.out.println("Importing '" +
+                                  artifact.getFile().getName() +
+                                  "' for schema '" + name + "'...");
             }
             else if (mutable)
             {
@@ -453,7 +461,7 @@
          if (useDfFile)
          {
             // the DF file exists, so read it
-            isr = new InputStreamReader(new FileInputStream(file), charset);
+            isr = new InputStreamReader(new FileInputStream(artifact.getFile()), charset);
          }
          else if (mutable)
          {
@@ -461,21 +469,19 @@
             isr = new InputStreamReader(new ByteArrayInputStream(emptyDfFooter.getBytes()));
          }
          in  = new BufferedReader(isr);
-         ProgressLexer lexer = new ProgressLexer(in, resolver);
+         SourceArtifact sourceArtifact = ArtifactManager.addLexerArtifact(artifact.getRelativePath(), in);
+         ProgressLexer lexer = new ProgressLexer(sourceArtifact, resolver);
          lexer.activateSchemaProcessing();
          
          // Parse data and create a tree
          SchemaParser parser = new SchemaParser(lexer, true);
          parser.schema();
          
-         File schemaFile = config.getSchemaFile(name, AstGenerator.DICT_POSTFIX);
-         
-         String filename = schemaFile.getAbsolutePath();
-         String normal   = Configuration.normalizeFilename(filename);
+         Artifact schemaArtifact = config.getSchemaFile(name, AstGenerator.DICT_POSTFIX);
          
          // Get the root AST, register it, and fix it up.
          root = (Aast) parser.getAST();
-         root.brainwash(normal);
+         root.brainwash(schemaArtifact);
          AstManager.get().setFlag(root.getId(), AstGenerator.FLAG_DICT);
          
          // Replace the unknown database name with the actual name.
@@ -483,8 +489,7 @@
          
          // Store the original .df filename as an annotation in the database
          // node.
-         String actual  = file.getCanonicalPath();
-         String srcfile = Configuration.normalizeFilename(actual);
+         String srcfile = sourceArtifact.getRelativePath();
          root.putAnnotation("srcfile", srcfile);
       }
       catch (Exception exc)
@@ -643,14 +648,16 @@
       String name = ast.getText();
       try
       {
-         File schemaFile = config.getSchemaFile(name, AstGenerator.DICT_POSTFIX);
-         String normalized = Configuration.normalizeFilename(schemaFile.getCanonicalPath());
-         
-         AstManager.get().saveTree(ast, normalized, false);
+         Artifact schemaArtifact = config.getSchemaFile(name, AstGenerator.DICT_POSTFIX);
+         AstManager.get().saveTree(ast, schemaArtifact, false);
          
          if (!quietMode)
          {
-            System.out.println("Persisted schema '" + name + "' to '" + schemaFile.getName() + "'");
+            System.out.println("Persisted schema '" +
+                               name +
+                               "' to '" +
+                               schemaArtifact.getRelativePath() +
+                               "'");
          }
       }
       catch (Exception exc)
@@ -831,24 +838,20 @@
    /**
     * Load the specified database AST from persistence.
     *
-    * @param   file
-    *          File containing the database AST to be loaded.
+    * @param   artifact
+    *          Artifact containing the database AST to be loaded.
     *
     * @return  AST representing the schema.
     *
     * @throws  SchemaException
     *          if any error occurs reading the AST from persistence.
     */
-   public Aast loadAst(File file)
+   public Aast loadAst(Artifact artifact)
    throws SchemaException
    {
       try
       {
-         // Read AST from persistence.
-         String fname = file.getCanonicalPath();
-         fname = Configuration.normalizeFilename(fname);
-         
-         return AstManager.get().loadTree(fname);
+         return AstManager.get().loadTree(artifact);
       }
       catch (Exception exc)
       {
@@ -875,10 +878,10 @@
       {
          if (Configuration.isRuntimeConfig() || Configuration.isImportMode())
          {
-            String resourceName = config.getSchemaResource(name, AstGenerator.DICT_POSTFIX);
-            if (resourceName != null)
+            Artifact artifact = config.getSchemaResource(name, AstGenerator.DICT_POSTFIX);
+            if (artifact != null)
             {
-               return AstManager.get().loadTree(resourceName);
+               return AstManager.get().loadTree(artifact);
             }
             else
             {
@@ -887,8 +890,8 @@
          }
          else
          {
-            File file = config.getSchemaFile(name, AstGenerator.DICT_POSTFIX);
-            return loadAst(file);
+            Artifact artifact = config.getSchemaFile(name, AstGenerator.DICT_POSTFIX);
+            return loadAst(artifact);
          }
       }
       catch (Exception exc)

=== modified file 'src/com/goldencode/p2j/schema/SchemaWorker.java'
--- old/src/com/goldencode/p2j/schema/SchemaWorker.java	2025-03-19 13:44:21 +0000
+++ new/src/com/goldencode/p2j/schema/SchemaWorker.java	2025-05-09 07:59:16 +0000
@@ -92,6 +92,9 @@
 **                           metaTablesInPersistentDB so that they get moved from standard to primary dbs.
 ** 034 CA  20250319          This worker can be used before the 'qualified_oo_name' is set in annotation 
 **                           (like in schema conversion phase), so ensure we can load the ClassDefinition. 
+** 035 DDF 20250408          Replace usage of a filename with an artifact in loadFromPersistence().
+**     DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250423          getSchemaFileName() return an artifact.
 */
 
 /*
@@ -154,6 +157,8 @@
 import java.util.function.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -294,21 +299,21 @@
     *
     * @param   dictionary
     *          The schema dictionary instance.
-    * @param   file
-    *          The name of a persisted schema file which is associated with a Progress source code file.
+    * @param   artifact
+    *          The artifact of a persisted schema file which is associated with a Progress source code file.
     *
     * @return  an instance of {@code SchemaException} if any error occurs reading the schema data from
     *          persistence and {@code null} on success.
     */
-   public static SchemaException loadFromPersistence(SchemaDictionary dictionary, String file)
+   public static SchemaException loadFromPersistence(SchemaDictionary dictionary, Artifact artifact)
    {
-      String fname = adjustFilename(file, AstGenerator.DICT_POSTFIX);
-      File   fil   = new File(fname);
-      if (fil.exists())
+      String fname = adjustFilename(artifact.getRelativePath(), AstGenerator.DICT_POSTFIX);
+      Artifact dictArtifact = ArtifactManager.addArtifact(fname);
+      if (dictArtifact.exists())
       {
          try
          {
-            dictionary.loadFromPersistence(fil.getAbsolutePath());
+            dictionary.loadFromPersistence(dictArtifact);
          }
          catch (SchemaException e)
          {
@@ -386,15 +391,15 @@
     *
     * @param   schemaRoot
     *          AST which represents a permanent or transient database schema.
-    * @param   file
-    *          File to which schema AST is saved after updates have been made.
+    * @param   artifact
+    *          Artifact to which schema AST is saved after updates have been made.
     * @param   tables
     *          Map of normalized table names to table ASTs.
     * @param   indexes
     *          Map of normalized index names to index ASTs.
     */
    private void persistSchema(Aast schemaRoot,
-                              File file,
+                              Artifact artifact,
                               Map<String, Aast> tables,
                               Map<String, Aast> indexes)
    throws SchemaException
@@ -421,8 +426,7 @@
       try
       {
          // Persist modified AST.
-         String filename = file.getAbsolutePath();
-         AstManager.get().saveTree(schemaRoot, filename, false);
+         AstManager.get().saveTree(schemaRoot, artifact, false);
       }
       catch (Exception exc)
       {
@@ -674,8 +678,8 @@
        * databases loaded. Also checks for hints to see if any additional
        * databases need to be loaded and/or aliases assigned.
        *
-       * @param   filename
-       *          Name of the source file represented by the AST currently
+       * @param   artifact
+       *          Artifact of the source file represented by the AST currently
        *          being processed in the pattern engine. Any temp- and work-
        *          tables defined in this source file are loaded into the
        *          dictionary.
@@ -686,16 +690,14 @@
        *          if any error occurs loading source file-specific schema
        *          information.
        */
-      public boolean initializeDictionary(String filename)
+      public boolean initializeDictionary(Artifact artifact)
       throws SchemaException
       {
-         String originalFileName = filename;
-         
          // Get a clean instance of the dictionary.
          dictionary = new SchemaDictionary((Set<String>) null);
          
          // Load hints for this source file, if any.
-         UastHints hints = new UastHints(filename);
+         UastHints hints = new UastHints(artifact.getRelativePath());
          
          // load extra databases into dictionary, if any
          Set<String> dbList = hints.getDatabases();
@@ -732,7 +734,7 @@
             {
                if (exc[0] == null)
                {
-                  exc[0] = loadFromPersistence(dictionary, cls.getFilename());
+                  exc[0] = loadFromPersistence(dictionary, cls.getArtifact());
                }
             };
             
@@ -745,10 +747,11 @@
          }
          
          // Load AST which defines temp- and work-tables for the current source file.
-         filename = Configuration.normalizeFilename(originalFileName + AstGenerator.DICT_POSTFIX);
-         if (AstManager.get().isExistingAst(filename))
+         Artifact dictArtifact = ArtifactManager.addArtifact(artifact.getRelativePath() +
+                                                             AstGenerator.DICT_POSTFIX);
+         if (AstManager.get().isExistingAst(dictArtifact))
          {
-            Aast root = AstManager.get().loadTree(filename);
+            Aast root = AstManager.get().loadTree(dictArtifact);
             dictionary.loadFromAst(root, false);
          }
          
@@ -1007,7 +1010,7 @@
              SchemaException
       {
          SchemaConfig schemaConfig = Configuration.getSchemaConfig();
-         Map<File, Aast> schemaMap = new LinkedHashMap<>();
+         Map<Artifact, Aast> schemaMap = new LinkedHashMap<>();
          
          // first pass collects permanent tables and indexes from databases participating in
          // natural joins
@@ -1025,23 +1028,23 @@
                next = schemaConfig.getMetadata().getName();
             }
             
-            String filename = schemaConfig.getSchemaFileName(next, SCHEMA_POSTFIX);
-            File saveFile = new File(filename);
-            File loadFile = getSchemaLoadFile(filename);
-            if (loadFile.exists())
+            Artifact saveArtifact = schemaConfig.getSchemaFileName(next, SCHEMA_POSTFIX);
+            File loadFile = getSchemaLoadFile(saveArtifact.getRelativePath());
+            Artifact artifact = ArtifactManager.addArtifact(loadFile.toString());
+            if (artifact.exists())
             {
-               Aast schemaRoot = loader.loadAst(loadFile);
-               schemaMap.put(saveFile, schemaRoot);
+               Aast schemaRoot = loader.loadAst(artifact);
+               schemaMap.put(saveArtifact, schemaRoot);
                gatherTableAndIndexAsts(schemaRoot, tables, indexes);
             }
          }
          
          // second pass attaches join data and persists the schema ASTs
-         for (Map.Entry<File, Aast> entry : schemaMap.entrySet())
+         for (Map.Entry<Artifact, Aast> entry : schemaMap.entrySet())
          {
-            File saveFile = entry.getKey();
+            Artifact saveArtifact = entry.getKey();
             Aast schemaRoot = entry.getValue();
-            persistSchema(schemaRoot, saveFile, tables, indexes);
+            persistSchema(schemaRoot, saveArtifact, tables, indexes);
          }
          
          // process temp-table schemas
@@ -1049,16 +1052,17 @@
          {
             filename = adjustFilename(filename, SCHEMA_POSTFIX);
             File loadFile = getSchemaLoadFile(filename);
-            if (loadFile.exists())
+            Artifact artifact = ArtifactManager.addArtifact(loadFile.toString());
+            if (artifact.exists())
             {
                Map<String, Aast> tempTables = new HashMap<>();
                Map<String, Aast> tempIndexes = new HashMap<>();
-               Aast schemaRoot = loader.loadAst(loadFile);
+               Aast schemaRoot = loader.loadAst(artifact);
                gatherTableAndIndexAsts(schemaRoot, tempTables, tempIndexes);
                tables.putAll(tempTables);
                try
                {
-                  persistSchema(schemaRoot, loadFile, tables, tempIndexes);
+                  persistSchema(schemaRoot, artifact, tables, tempIndexes);
                }
                finally
                {

=== modified file 'src/com/goldencode/p2j/uast/AstGenerator.java'
--- old/src/com/goldencode/p2j/uast/AstGenerator.java	2025-04-22 06:57:43 +0000
+++ new/src/com/goldencode/p2j/uast/AstGenerator.java	2025-05-09 07:59:18 +0000
@@ -263,6 +263,8 @@
 import java.util.logging.*;
 
 import antlr.*;
+
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -912,26 +914,27 @@
    }
    
    /**
-    * Mark the filename's AST as 'original'.  This is used by already parsed .cls files, from pre-scan.
+    * Mark the artifact relative path AST as 'original'.  This is used by already parsed .cls files,
+    * from pre-scan.
     * 
-    * @param   filename
-    *          Name of the source file to mark.
+    * @param   artifact
+    *          The artifact of the source file to mark.
     */
-   public void markOriginal(String filename)
+   public void markOriginal(Artifact artifact)
    {
       try
       {
-         mgr.lockTree(filename, true);
-         long id = mgr.getTreeId(filename);
+         artifact.lock();
+         long id = mgr.getTreeId(artifact);
          mgr.setFlag(id, AstManager.FLAG_ORIGINAL);
       }
       catch (Exception exc)
       {
-         throw new AstException("Error marking as original " + filename, exc);
+         throw new AstException("Error marking as original " + artifact.getRelativePath(), exc);
       }
       finally
       {
-         mgr.unlockTree(filename);
+         artifact.unlock();
       }
    }
    
@@ -972,11 +975,11 @@
     * it is assumed that the file is not a built-in 4GL class, nor is it a
     * .NET class.
     *
-    * @param   filename
-    *          Name of the source file to process; if this is not an absolute
+    * @param   artifact
+    *          Artifact of the source file to process; if this is not an absolute
     *          path, it should be relative to the propath.
-    * @param   list
-    *          The list of files upon which the generator is iterating in the
+    * @param   col
+    *          The collection of artifacts upon which the generator is iterating in the
     *          calling method. The list may be edited as noted above. This
     *          may be <code>null</code> if no such editing is needed.
     * @param   idx
@@ -989,10 +992,10 @@
     *          if the file cannot be found, or if there is some other error
     *          during processing.
     */
-   public Aast processFile(String filename, List<File> list, int idx)
+   public Aast processFile(Artifact artifact, ArtifactCollection col, int idx)
    throws AstException
    {
-      filename = Configuration.normalizeFilename(filename);
+      String filename = artifact.getRelativePath();
 
       // remember the original file name
       String original = filename;
@@ -1001,17 +1004,17 @@
       {
          // exclude all other access to this file
          // TODO: doesn't this locking need to be on filename.ast instead of just filename?
-         mgr.lockTree(filename, list != null);
-         
+         artifact.lock();
+
          // should we honor a previously persisted AST?
-         if (astPersist && !ConversionData.mustConvertFile(filename))
+         if (astPersist && !((BaseArtifact) artifact).mustConvert())
          {
             // check if the AST exists and has a later timestamp than the
             // source file
             if (checkForValidAst(filename))
             {
                // load the AST and return it
-               return loadPersistedAst(filename);
+               return loadPersistedAst(artifact);
             }
          }
          
@@ -1033,20 +1036,14 @@
             
             // drive the web preprocessor
             preprocessE4GL(e4gl, original, filename);
+            Artifact newArtifact = ArtifactManager.addArtifact(filename);
             
             // rewrite the tree locking filename
-            mgr.rewriteTreeName(original, filename);
+            mgr.rewriteTreeArtifact(artifact, newArtifact);
             
             // make sure that caller's file list is updated to avoid
             // containing both the html and the generated .p/.w/.i
-            if (list != null)
-            {
-               removeDuplicate(list, filename);
-               
-               // set the current list element to be the output filename
-               // so that any downstream processing can find the AST
-               list.set(idx, new File(filename));
-            }
+            col.remove(artifact);
          }
          
          // load hints if they are available (must be done before propath
@@ -1059,7 +1056,7 @@
          // setup propath from configuration
          setPaths(null);
          
-         Aast root = parse(filename, hints.getBannedDatabases());
+         Aast root = parse(artifact, hints.getBannedDatabases());
          if (astPersist)
          {
             // this is not pre-scan, save the file hash
@@ -1083,24 +1080,11 @@
          hints = null;
          
          // clear our lock
-         mgr.unlockTree(filename);
+         artifact.unlock();
       }
    }
    
    /**
-    * Given a 4GL source code filename return the name of the AST file.
-    *
-    * @param    filename
-    *           The original source filename.
-    *
-    * @return   The target AST filename.
-    */   
-   public static String getAstName(String filename)
-   {
-      return filename + AST_POSTFIX;
-   }
-   
-   /**
     * Builds a unique filename based on an original filename and a passed
     * postfix.  This method takes the original filename and appends a fixed
     * value to create a new, unique cache filename.
@@ -1124,22 +1108,22 @@
     * resulting data buffer. If inline preprocessing is turned off, the input
     * stream is opened directly on the specified file.
     *
-    * @param   filename
-    *          Name of the source file to process; if this is not an absolute
+    * @param   artifact
+    *          Artifact of the source file to process; if this is not an absolute
     *          path, it should be relative to the propath.
     *
     * @return  Parser-ready input stream.
     *
     * @throws  FileNotFoundException
-    *          if <code>filename</code> does not exist.
+    *          if <code>artifact</code> file does not exist.
     * @throws  IOException
     *          If a problem occurs during preprocessing or caching. 
     */
-   private Reader prepareDataStream(String filename)
+   private Reader prepareDataStream(Artifact artifact)
    throws FileNotFoundException,
           IOException
    {
-      File file = Utils.findFileInPath(filename, paths);
+      File file = Utils.findFileInPath(artifact.getRelativePath(), paths);
       Reader in = null;
       
       if (preprocess)
@@ -1475,35 +1459,10 @@
    }
    
    /**
-    * Search the list to find a duplicate to the given name and remove it
-    * if found. Once the first matching entry is found, it will be set to
-    * <code>null</code> and the method will return.
-    *
-    * @param    list
-    *           Array to edit.
-    * @param    duplicate
-    *           Name of the file to remove. This may be <code>null</code>
-    *           but then why would you call this method?
-    */
-   private void removeDuplicate(List<File> list, String duplicate)
-   {
-      int size = list.size();
-      for (int i = 0; i < size; i++)
-      {
-         File file = list.get(i);
-         if (file != null && file.toString().equals(duplicate))
-         {
-            list.set(i, null);
-            return;
-         }
-      }
-   }
-   
-   /**
     * Performs first-level scanning of OO classes.
     * 
-    * @param   filename
-    *          Name of the source file to process; if this is not an absolute
+    * @param   artifact
+    *          Artifact of the source file to process; if this is not an absolute
     *          path, it should be relative to the propath.
     * @param   builtin
     *          <code>true</code> if this class file represents a built-in class or interface.
@@ -1515,25 +1474,23 @@
     * @throws  AstException
     *          if the file cannot be found, or if there is some other error during processing.
     */
-   void preScanClass(String filename, boolean builtin, boolean dotnet, String[] propath)
+   void preScanClass(Artifact artifact, boolean builtin, boolean dotnet, String[] propath)
    throws AstException,
           ANTLRException,
           IOException
    {
-      filename = Configuration.normalizeFilename(filename);
-      
       boolean caseSens = Configuration.getParameter(CASE_SENS_KEY, true);
       
       UastHints saveHints = hints;
       String[] savePaths = paths;
-      
+
       try
       {
          // exclude all other access to this file
-         mgr.lockTree(filename, false);
+         artifact.lock();
          
          // TODO: hints will not work, as this is called recursive... need a stack approach
-         hints = new UastHints(filename);
+         hints = new UastHints(artifact.getRelativePath());
          
          this.paths = propath;
          
@@ -1550,11 +1507,11 @@
          // 2nd pass (e.g. an instance method can be called in an expression
          // earlier in the file than where that method's definition appears)
          
-         ProgressLexer  lexer  = prepareLexer(filename, sym);
+         ProgressLexer  lexer  = prepareLexer(artifact, sym);
          ProgressParser parser = null;
          
          parser = new ProgressParser(lexer, sym);
-         parser.setFilename(filename);
+         parser.setFilename(artifact.getRelativePath());
          parser.setBuiltInCls(builtin);
          parser.setDotNetCls(dotnet);
          parser.pre_scan_class();
@@ -1568,7 +1525,7 @@
       
       catch (Exception exc)
       {
-         throw new AstException("Error processing " + filename, exc);
+         throw new AstException("Error processing " + artifact.getRelativePath(), exc);
       }
       
       finally
@@ -1580,7 +1537,7 @@
          hints = saveHints;
          
          // clear our lock
-         mgr.unlockTree(filename);
+         artifact.unlock();
       }
       
       // if everything was completed OK and this is a built-in class, do a full parse to save
@@ -1591,7 +1548,7 @@
          this.astPersist = true;
          try
          {
-            processFile(filename, null, -1);
+            processFile(artifact, null, -1);
          }
          finally
          {
@@ -1601,18 +1558,18 @@
    }
    
    /**
-    * Parse the source code read from the specified filename into an abstract
+    * Parse the source code read from the specified artifact into an abstract
     * syntax tree. Uses the Progress parser and its helpers to do this work.
     *
-    * @param   filename
-    *          The name of the file being processed.
+    * @param   artifact
+    *          The artifact of the file being processed.
     *
     * @return  Root of the AST created by the parser.
     *
     * @throws  ANTLRException
     *          if any error occurs while lexing or parsing the source code.
     */
-   private Aast parse(String filename, Set<String> bannedSchemas)
+   private Aast parse(Artifact artifact, Set<String> bannedSchemas)
    throws AstException,
           ANTLRException,
           IOException
@@ -1626,7 +1583,7 @@
       // the very first time we try to parse a class, we must ensure that the critical built-in classes
       // are already loaded; without this some classes (enums especially) can cause a parsing failure
       // during pre-scanning which is avoided when these same classes are already loaded
-      if (filename.endsWith(SymbolResolver.CLASS_EXT))
+      if (artifact.endsWith(SymbolResolver.CLASS_EXT))
       {
          synchronized (AstGenerator.class)
          {
@@ -1638,11 +1595,11 @@
          }
       }
       
-      ProgressLexer  lexer  = prepareLexer(filename, sym);
+      ProgressLexer  lexer  = prepareLexer(artifact, sym);
       ProgressParser parser = null;
       
       parser = new ProgressParser(lexer, sym);
-      parser.setFilename(filename);
+      parser.setFilename(artifact.getRelativePath());
       
       // Store off our AstGenerator instance RFB20190911
       parser.setGenerator(this);
@@ -1658,7 +1615,7 @@
                                   "in file '%s' with token text '%s').\n",
                                next.getLine(),
                                next.getColumn(),
-                               filename,
+                               artifact,
                                next.getText()));
       }
       
@@ -1667,7 +1624,7 @@
       
       // makes sure that parents and IDs are properly assigned (this is a
       // form of post-processing)
-      result.brainwash(filename, true);
+      result.brainwash(artifact, true);
       
       if (result.downPath(ProgressParser.CLASS_DEF)     ||
           result.downPath(ProgressParser.INTERFACE_DEF) ||
@@ -1675,7 +1632,6 @@
       {
          // this is a class - save the AST IDs where is required
          boolean isClass = result.downPath(ProgressParser.CLASS_DEF);
-         boolean isEnum  = result.downPath(ProgressParser.ENUM_DEF);
          boolean isIface = result.downPath(ProgressParser.INTERFACE_DEF);
          int tok = isClass ? ProgressParser.CLASS_DEF 
                            : isIface ? ProgressParser.INTERFACE_DEF
@@ -1692,17 +1648,20 @@
       }
       // save off output if requested, THIS MUST BE DONE AFTER FIXUPS!
       if (dumpParser)
-         generateParseReport(filename, result);
+      {
+         generateParseReport(artifact, result);
+      }
       
       if (astPersist)
       {
-         persistAst(filename, result);
+         persistAst(artifact, result);
          
          // persist schema dictionary state
-         File schemaFile = createUniqueFile(filename, DICT_POSTFIX);
+         File schemaFile = createUniqueFile(artifact.getRelativePath(), DICT_POSTFIX);
+         Artifact schemaArtifact = ArtifactManager.addArtifact(schemaFile.getCanonicalPath());
          try
          {
-            sym.persistSchemaData(filename, schemaFile.getAbsolutePath());
+            sym.persistSchemaData(artifact, schemaArtifact);
          }
          catch (Exception exc)
          {
@@ -1725,35 +1684,35 @@
     * Prepare a lexer instance for the given file using the supporting
     * resolver instance as needed.
     *
-    * @param    filename
-    *           The file being lexed.
+    * @param    artifact
+    *           The artifact being lexed.
     * @param    sym
     *           The symbol resolver instance being used for this file.
     *
-    * @return   A token stream source for the given file.
+    * @return   A token stream source for the given artifact.
     */
-   private ProgressLexer prepareLexer(String filename, SymbolResolver sym)
+   private ProgressLexer prepareLexer(Artifact artifact, SymbolResolver sym)
    throws IOException
    {
       // preprocess file or load source from cache
-      Reader in = prepareDataStream(filename);
+      Reader in = prepareDataStream(artifact);
       
       ProgressLexer lexer = null;
-      
+      SourceArtifact sourceArtifact = ArtifactManager.addLexerArtifact(artifact.getRelativePath(), in);
       if (dumpLexer)
       {
-         File lexerFile = createUniqueFile(filename, LEXER_POSTFIX);
+         File lexerFile = createUniqueFile(sourceArtifact.getRelativePath(), LEXER_POSTFIX);
          
          if (lexerFile.exists() && lexerFile.isFile())
          {
             lexerFile.delete();
          }
          
-         lexer = new LexerDumpFilter(in, sym, lexerFile);
+         lexer = new LexerDumpFilter(sourceArtifact, sym, lexerFile);
       }
       else
       {
-         lexer = new ProgressLexer(in, sym);
+         lexer = new ProgressLexer(sourceArtifact, sym);
       }
       
       // keyword ignore support must be implemented after the lexer is
@@ -1895,14 +1854,14 @@
     * is generated off the original source file's name, with a standard
     * extension of <code>PARSER_POSTFIX</code>.
     *
-    * @param    filename
-    *           Original source file being processed.
+    * @param    artifact
+    *           Original source artifact being processed.
     * @param    ast
     *           The AST to dump.
     */
-   private void generateParseReport(String filename, Aast ast)
+   private void generateParseReport(Artifact artifact, Aast ast)
    {  
-      PrintStream out     = createUniqueStream(filename, PARSER_POSTFIX);
+      PrintStream out     = createUniqueStream(artifact.getRelativePath(), PARSER_POSTFIX);
       DumpTree    visitor = new DumpTree(out);
       
       visitor.visit(ast);
@@ -1915,25 +1874,25 @@
     * original source file's name, with a standard extension of
     * <code>AST_POSTFIX</code>.
     *
-    * @param    filename
-    *           Original source file being processed.
+    * @param    artifact
+    *           Original source artifact being processed.
     * @param    ast
     *           The AST to dump.
     */
-   private void persistAst(String filename, Aast ast)
+   private void persistAst(Artifact artifact, Aast ast)
    throws AstException
    {
-      File   target = createUniqueFile(filename, AST_POSTFIX);
-      String fname  = null;
+      File   target = createUniqueFile(artifact.getRelativePath(), AST_POSTFIX);
+      Artifact fname  = null;
       
       try
       {
-         fname = target.getCanonicalPath();
+         fname = ArtifactManager.addArtifact(target.getCanonicalPath());
       }
       
       catch (IOException ioe)
       {
-         throw new AstException("Invalid filename " + filename, ioe);
+         throw new AstException("Invalid filename " + artifact.getRelativePath(), ioe);
       }
       
       AstManager.get().saveTree(ast, fname, false);
@@ -1960,10 +1919,10 @@
    }
    
    /**
-    * Loads a previously persisted AST for a specified source file.
+    * Loads a previously persisted AST for a specified source artifact.
     *
-    * @param    filename
-    *           The source filename for which a matching persisted AST will 
+    * @param    artifact
+    *           The source artfact for which a matching persisted AST will
     *           be loaded.
     *
     * @return   The AST associated with the specified source file.
@@ -1971,21 +1930,21 @@
     * @throws   AstException
     *           if there are any problems with loading the AST.
     */
-   private Aast loadPersistedAst(String filename)
+   private Aast loadPersistedAst(Artifact artifact)
    throws AstException
    {
-      File source = createUniqueFile(filename, AST_POSTFIX);
+      File source = createUniqueFile(artifact.getRelativePath(), AST_POSTFIX);
       
-      String fname  = null;
+      Artifact fname  = null;
       
       try
       {
-         fname = source.getCanonicalPath();
+         fname = ArtifactManager.addArtifact(source.getCanonicalPath());
       }
       
       catch (IOException ioe)
       {
-         throw new AstException("Invalid filename " + filename, ioe);
+         throw new AstException("Invalid filename " + artifact.getRelativePath(), ioe);
       }
       
       return AstManager.get().loadTree(fname);

=== modified file 'src/com/goldencode/p2j/uast/AstKey.java'
--- old/src/com/goldencode/p2j/uast/AstKey.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/uast/AstKey.java	2025-05-09 07:59:18 +0000
@@ -2,7 +2,7 @@
 ** Module   : AstKey.java
 ** Abstract : a container for an AST which can be used as a hash key
 **
-** Copyright (c) 2006-2023, Golden Code Development Corporation.
+** Copyright (c) 2006-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------Description-----------------
 ** 001 ECF 20060201   @24281 Created initial version. A container for an
@@ -25,7 +25,10 @@
 ** 008 CA  20230215          Fixed a bug in writeExternal - if the ast is null, write the astId, as the AST 
 **                           may not have needed to be loaded for this AstKey instance.
 ** 009 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 010 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250424          Renamed getTreeName() to getTreeArtifact().
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -86,6 +89,7 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.p2j.pattern.*;
@@ -675,7 +679,7 @@
       {
          AstManager mgr = AstManager.get();
          long treeId = mgr.getTreeId(astId);
-         Aast tree = mgr.loadTree(mgr.getTreeName(treeId));
+         Aast tree = mgr.loadTree(mgr.getTreeArtifact(treeId));
          resolver.registerTree(tree.duplicate());
          ast = resolver.getAst(astId);
       }
@@ -688,9 +692,9 @@
     * methods against two P2O ASTs.
     *
     * @param   args
-    *          Two filenames are expected (the persisted ASTs to be loaded).
+    *          Two artifacts are expected (the persisted ASTs to be loaded).
     */
-   public static void main(String[] args)
+   public static void main(Artifact[] args)
    {
       try
       {

=== modified file 'src/com/goldencode/p2j/uast/CallGraphWorker.java'
--- old/src/com/goldencode/p2j/uast/CallGraphWorker.java	2024-04-17 10:14:07 +0000
+++ new/src/com/goldencode/p2j/uast/CallGraphWorker.java	2025-05-09 07:59:18 +0000
@@ -3,7 +3,7 @@
 ** Abstract : provides a pattern engine service to create a new call graph based on matches
 **            found during AST processing
 **
-** Copyright (c) 2005-2024, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
 ** 001 GES 20050311   @20310 Created initial version that provides support for creating the call
@@ -76,6 +76,10 @@
 **                           InMemoryRegistryPlugin is not thread-safe.
 ** 027 TJD 20240208          Java 17 dependencies updates
 **     TJD 20240216          Newer versions of JanusDB require MixedTypeIndexes when using aggregates
+** 028 GES 20230524          Moved some code to a more generic package.
+**     DDF 20250327          Replaced usage of FileSpecList with ArtifactCollection.
+**     DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250424          Renamed getTreeName() to getTreeArtifact().
 */
 
 /*
@@ -139,6 +143,7 @@
 import java.util.concurrent.atomic.*;
 import java.util.function.*;
 
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.graphdb.*;
 import com.goldencode.p2j.cfg.*;
@@ -149,7 +154,6 @@
 
 import org.janusgraph.graphdb.types.system.SystemTypeManager;
 import org.apache.tinkerpop.gremlin.structure.*;
-import org.apache.tinkerpop.gremlin.process.traversal.*;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.*;
 import static org.janusgraph.core.attribute.Text.*;
 
@@ -457,10 +461,10 @@
       {
          Vertex node = itr.next();
          
-         String filename = mgr.getTreeName((long) node.<Long>property("node-id").value());
-         if (filename != null)
+         Artifact artifact = mgr.getTreeArtifact((long) node.<Long>property("node-id").value());
+         if (artifact != null)
          {
-            filename = CallGraphHelper.prepareFilename(filename);
+            String filename = CallGraphHelper.prepareFilename(artifact.getRelativePath());
             Vertex procFile = CallGraphHelper.findUniqueNode("filename", filename);
             
             if (!ambigExtProgs.contains(filename) && procFile.property("finished").isPresent())
@@ -826,7 +830,7 @@
          
          String includeSource = hints.getSourceFor(line, column);
          includeSource = CallGraphHelper.prepareFilename(includeSource);
-         String filename = CallGraphHelper.prepareFilename(ast.getFilename());
+         String filename = CallGraphHelper.prepareFilename(ast.getArtifact().getRelativePath());
 
          return (includeSource.equals(filename)) ? null : includeSource;
       }
@@ -1056,20 +1060,20 @@
        *           The label for the new vertex.
        * @param    type
        *           The "node-type" property for the new vertex.
-       * @param    filename
-       *           The OS-level filename of the resource.
+       * @param    artifact
+       *           The OS-level artifact of the resource.
        *           
        * @return   The created {@link Vertex node}.
        */
-      public Vertex createFileResource(String label, int type, String filename)
+      public Vertex createFileResource(String label, int type, Artifact artifact)
       {
          long   id    = nextExternalId(type);
          Vertex fnode = createNode(label, id, type, FILE_RESOURCE);
          
          // need to use linux-style separators
-         String fullName = CallGraphHelper.prepareFilename(filename);
+         String fullName = CallGraphHelper.prepareFilename(artifact.getRelativePath());
          
-         fnode.<String>property("os-filename", filename);
+         fnode.<String>property("os-filename", artifact.getRelativePath());
          fnode.<String>property("filename", fullName);
          fnode.<String>property("reverse-filename", reverseString(fullName));
          fnode.<String>property("node-key", "filename");
@@ -2286,18 +2290,21 @@
          String spec     = Configuration.getParameter("include-spec", "*.[iI]");
          
          System.out.println(String.format("Using %s for include-file specifications.", spec));
-         
-         FileSpecList list     = new FileSpecList(new File(basepath), spec, true);
-         File[]       includes = list.list();
-         
-         for (File fi : includes)
+
+         ArtifactCollection col = ArtifactManager.createArtifactsByFileList(FileSpecList.class,
+                                                                            new File(basepath),
+                                                                            spec,
+                                                                            true);
+         Iterator<Artifact> iter = col.iterator();
+         while (iter.hasNext())
          {
-            String filename = fi.getPath();
+            Artifact artifact = iter.next();
+            String filename = artifact.getRelativePath();
             System.out.println(filename);
             Vertex vnode = CallGraphHelper.findUniqueNode("os-filename", filename);
             if (vnode == null)
             {
-               createIncludeFile(filename);
+               createIncludeFile(artifact);
             }
          }
          
@@ -2552,9 +2559,9 @@
             try
             {
                Aast src = getResolver().getSourceAst();
-               File file = Configuration.toFile(src.getFilename());
+               Artifact artifact = src.getArtifact();
 
-               hints = new UastHints(file.getAbsolutePath());
+               hints = new UastHints(artifact.getFile().getAbsolutePath());
             }
 
             catch (Exception exc)
@@ -2570,24 +2577,25 @@
       }
 
       /**
-       * Create an include file vertex for the given filename.  This will also check if there is
-       * an external program file already parsed, with the same filename.  If so, it will create
+       * Create an include file vertex for the given artifact. This will also check if there is
+       * an external program file already parsed, with the same artifact. If so, it will create
        * a program file, not an include file.
        *  
-       * @param    filename
-       *           The name of the file resource.
+       * @param    artifact
+       *           The artifact of the file resource.
        */
-      public Vertex createIncludeFile(String filename)
+      public Vertex createIncludeFile(Artifact artifact)
       {
          try
          {
-            AstManager.get().loadTree(filename + ".ast");
+            Artifact astArtifact = ArtifactManager.addArtifact(artifact.getRelativePath() + ".ast");
+            AstManager.get().loadTree(astArtifact);
             
             // this is a program file
-            Vertex vProg = findUniqueNode("os-filename", filename);
+            Vertex vProg = findUniqueNode("os-filename", artifact.getRelativePath());
             if (vProg == null)
             {
-               vProg = createFileResource("Procedure File", PROCEDURE_FILE, filename);
+               vProg = createFileResource("Procedure File", PROCEDURE_FILE, artifact);
             }
             
             return vProg;
@@ -2598,7 +2606,7 @@
          }
          
          // it is a real include file
-         return createFileResource("Include File", INCLUDE_FILE, filename);
+         return createFileResource("Include File", INCLUDE_FILE, artifact);
       }
 
       /**
@@ -3073,7 +3081,8 @@
             }
             
             Vertex root = db.traversal().V()
-                            .has("filename", CallGraphHelper.prepareFilename(ast.getFilename()))
+                            .has("filename",
+                                 CallGraphHelper.prepareFilename(ast.getArtifact().getRelativePath()))
                             .next();
             String parentLabelName = CallGraphHelper.parentProperty(PROCEDURE_FILE);
             property(node, parentLabelName, root.id());
@@ -3093,13 +3102,13 @@
       {
          if (pphints == null)
          {
-            pphints = new PreprocessorHints(ast.getFilename());
+            pphints = new PreprocessorHints(ast.getArtifact().getRelativePath());
          }
          else
          {
-            if (!ast.getFilename().equals(pphints.getSource()))
+            if (!ast.getArtifact().getRelativePath().equals(pphints.getSource()))
             {
-               return new PreprocessorHints(ast.getFilename());
+               return new PreprocessorHints(ast.getArtifact().getRelativePath());
             }
          }
          
@@ -3143,7 +3152,8 @@
                System.out.println(String.format(msg, hint.getFilename()));
 
                // create it
-               vnode = createIncludeFile(hintfile);
+               Artifact artifact = ArtifactManager.addArtifact(hintfile);
+               vnode = createIncludeFile(artifact);
             }
             
             Edge edge = createEdge(parent, vnode, "includes");
@@ -3210,4 +3220,4 @@
          return next.get();
       }
    }
-}
\ No newline at end of file
+}

=== modified file 'src/com/goldencode/p2j/uast/ClassDefinition.java'
--- old/src/com/goldencode/p2j/uast/ClassDefinition.java	2025-03-31 13:41:45 +0000
+++ new/src/com/goldencode/p2j/uast/ClassDefinition.java	2025-05-09 07:59:18 +0000
@@ -188,6 +188,8 @@
 **                           provisional - this is the only case allowed in 4GL, when a var can be defined
 **                           after it is referenced.
 ** 028 AS  20250331          Fixed a typo in exactMethodLookup.
+** 029 DDF 20250408          Replaced usage of direct filename with an artifact.
+**     DDF 20250414          Make use of artifacts instead of the filename.
 */
 
 /*
@@ -251,6 +253,8 @@
 import java.util.logging.*;
 import java.util.stream.Collectors;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.p2j.convert.db.*;
@@ -338,8 +342,8 @@
    /** Flag marking the type of this OO 4GL compile unit. */
    private final OOType otype;
    
-   /** The filename of the .cls that defined this class/interface. */
-   private final String filename;
+   /** The artifact of the .cls that defined this class/interface. */
+   private final Artifact artifact;
    
    /** Temporary ID number (before AST IDs are assigned). */
    private int tempIdx = 0;
@@ -396,8 +400,8 @@
     * @param    dotnet
     *           <code>true</code> if this is a .NET definition,
     *           <code>false</code> if this is a 4GL definition.
-    * @param    filename
-    *           The filename of the .cls that defined this class/interface.
+    * @param    artifact
+    *           The artifact of the .cls that defined this class/interface.
     */
    public ClassDefinition(String            name,
                           ClassDefinition[] parents,
@@ -405,7 +409,7 @@
                           boolean           builtin,
                           OOType            otype,
                           boolean           dotnet,
-                          String            filename)
+                          Artifact          artifact)
    {
       this.name       = name;
       this.parents    = parents;
@@ -414,7 +418,7 @@
       this.builtin    = builtin;
       this.otype      = otype;
       this.dotnet     = dotnet;
-      this.filename   = filename;
+      this.artifact   = artifact;
 
       if (interfaces != null)
       {
@@ -452,12 +456,13 @@
    {
       this.processed = true;
       this.name = qname;
-      this.filename = ConversionData.findClassFilename(qname);
+      this.artifact = ArtifactManager.addArtifact(ConversionData.findClassFilename(qname));
       
       // load the root AST and load the following:
       // parents (1 for class, 1-n for iface)
       // interfaces (0-n only for class)
-      Aast root = AstManager.get().loadTree(filename + ".ast");
+      Artifact loadArtifact = ArtifactManager.addArtifact(artifact.getRelativePath() + ".ast");
+      Aast root = AstManager.get().loadTree(loadArtifact);
       
       // get from the root
       this.dotnet = Boolean.TRUE.equals(root.getAnnotation("dotnet-cls"));
@@ -474,7 +479,7 @@
       {
          this.schemaDict = new SchemaDictionary(null);
          this.schemaDict.addScope(false);
-         SchemaWorker.loadFromPersistence(schemaDict, filename);
+         SchemaWorker.loadFromPersistence(schemaDict, artifact);
          
          this.javaPackage   = (String) root.getAnnotation("pkgname");
          this.javaClassName = (String) root.getAnnotation("classname");
@@ -1670,13 +1675,13 @@
    }
    
    /**
-    * Obtains the filename of the .cls that defined this class/interface.
+    * Obtains the artifact of the .cls that defined this class/interface.
     *
-    * @return   Project relative .cls source file name.  
+    * @return   Project .cls source artifact.
     */
-   public String getFilename()
+   public Artifact getArtifact()
    {
-      return filename;
+      return artifact;
    }
    
    /**
@@ -1761,7 +1766,7 @@
    }
 
    /**
-    * The method indicates whether this inctance represents a "mock" class definition
+    * The method indicates whether this instance represents a "mock" class definition
     * used to resolve symbols during class pre-scan mode in case the class file has not been
     * parsed.
     *
@@ -1891,7 +1896,14 @@
          node.putAnnotation("static", Boolean.valueOf(isStatic));
          node.putAnnotation("oo-data-store", DataStoreType.METHOD.toString());
          node.putAnnotation("tempidx", Long.valueOf(mTempIdx));
-         node.putAnnotation("tempidx-file", this.filename);
+         if (this.artifact != null)
+         {
+            node.putAnnotation("tempidx-file", this.artifact.getRelativePath());
+         }
+         else
+         {
+            node.putAnnotation("tempidx-file", "");
+         }
          
          if (extent != 0)
          {
@@ -2066,7 +2078,7 @@
             }
             
             node.putAnnotation("tempidx", Long.valueOf(mdat.tempIdx));
-            node.putAnnotation("tempidx-file", mdat.container.filename);
+            node.putAnnotation("tempidx-file", mdat.container.artifact.getRelativePath());
             
             node.putAnnotation("found-in-cls", mdat.container.name);
             if (mdat.container.builtin)
@@ -2077,7 +2089,7 @@
             {
                node.putAnnotation("dotnet-cls", true);
             }
-            node.putAnnotation("found-in-source-file", mdat.container.filename);
+            node.putAnnotation("found-in-source-file", mdat.container.artifact.getRelativePath());
             
             if (mdat.extent != 0)
             {

=== modified file 'src/com/goldencode/p2j/uast/FrameAstKey.java'
--- old/src/com/goldencode/p2j/uast/FrameAstKey.java	2020-09-24 17:41:02 +0000
+++ new/src/com/goldencode/p2j/uast/FrameAstKey.java	2025-05-09 07:59:18 +0000
@@ -2,7 +2,7 @@
 ** Module   : FrameAstKey.java
 ** Abstract : a dedicated key for comparing (shared) frames definitions 
 **
-** Copyright (c) 2016-2020, Golden Code Development Corporation.
+** Copyright (c) 2016-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 OM  20161121 Initial release. 
@@ -10,7 +10,10 @@
 ** 003 CA  20200416 Reduce the memory footprint for incremental conversion.
 ** 004 CA  20200423 Keep a copy of the AST in memory, to not pin the entire file.
 ** 005 IAS 20200922 Get rid of possible NPE on serialization.
+** 006 DDF 20250414 Make use of artifacts instead of the filename.
+**     DDF 20250424 Renamed getTreeName() to getTreeArtifact().
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -69,6 +72,8 @@
 import static com.goldencode.util.NativeTypeSerializer.*; 
 import java.io.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.pattern.*;
 
@@ -258,7 +263,9 @@
       {
          AstManager mgr = AstManager.get();
          long treeId = mgr.getTreeId(astId);
-         Aast tree = mgr.loadTree(mgr.getTreeName(treeId) + ".ast");
+         Artifact artifact = ArtifactManager.addArtifact(mgr.getTreeArtifact(treeId).getRelativePath() +
+                                                         ".ast");
+         Aast tree = mgr.loadTree(artifact);
          resolver.registerTree(tree);
          ast = resolver.getAst(astId);
          resolver.deregisterTree(tree);

=== modified file 'src/com/goldencode/p2j/uast/JavaClassDefinition.java'
--- old/src/com/goldencode/p2j/uast/JavaClassDefinition.java	2025-03-25 15:07:52 +0000
+++ new/src/com/goldencode/p2j/uast/JavaClassDefinition.java	2025-05-09 07:59:18 +0000
@@ -26,6 +26,7 @@
 ** 009 CA  20250319 A provisional class var found during pre-scan, which is referenced before the definition
 **                  statement and is referenced via a chain, must be resolved even if is provisional - this is
 **                  the only case allowed in 4GL, when a var can be defined after it is referenced.
+** 010 DDF 20250408 Replace usage of cls.getName() with null, the value is not used.
 */
 
 /*
@@ -112,7 +113,7 @@
     */
    public JavaClassDefinition(Class<?> cls)
    {
-      super(cls.getName(), null, null, false, decodeOOType(cls), false, cls.getName());
+      super(cls.getName(), null, null, false, decodeOOType(cls), false, null);
       this.cls = cls;
    }
    

=== modified file 'src/com/goldencode/p2j/uast/JavaPatternWorker.java'
--- old/src/com/goldencode/p2j/uast/JavaPatternWorker.java	2022-10-28 10:54:33 +0000
+++ new/src/com/goldencode/p2j/uast/JavaPatternWorker.java	2025-05-09 07:59:18 +0000
@@ -2,7 +2,7 @@
 ** Module   : JavaPatternWorker.java
 ** Abstract : Pattern worker with specific knowledge of the Java language
 **
-** Copyright (c) 2005-2022, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 GES 20050324   @20599 Created initial version. Provides Java AST
@@ -52,7 +52,11 @@
 **                           All worker's state must be context-local, for runtime conversion to work in 
 **                           multi-context mode, as the pattern workers are singletons.
 **     TJD 20220504          Java 11 compatibility minor changes
+** 019 DDF 20250320          Replace usage Set<String> with ArtifactCollection for wa.pfiles.
+**     DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250424          Fix naming.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -110,6 +114,10 @@
 
 import java.io.*;
 import java.util.*;
+
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactCollection;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -227,92 +235,66 @@
     * root node.  A new file ID is registered in the {@link AstManager}
     * and set into the AST.
     *
-    * @param    file
-    *           The file in which to persist the new AST.
+    * @param    artifact
+    *           The artifact in which to persist the new AST.
     * @param    resolver
     *           The current instance of the {@link AstSymbolResolver} in use
     *           or <code>null</code>.
     *
     * @return   The root AST that was created or <code>null</code> on error.
     */   
-   public static JavaAst createRootAst(File file, AstSymbolResolver resolver)
+   public static JavaAst createRootAst(Artifact artifact, AstSymbolResolver resolver)
    {
       JavaAst result = createAst(COMPILE_UNIT,
                                  "compilation unit",
                                  null,
                                  null,
                                  -1);
-      initializeRootAst(result, file, resolver);
+      initializeRootAst(result, artifact, resolver);
       
       return result;
    }
    
    /**
-    * Worker routine to centralize creation of a new Java AST that is a root node.  A new file ID
-    * is registered in the {@link AstManager} and set into the AST.
-    *
-    * @param    filename
-    *           The name of the file in which to persist the new AST. Can be real or virtual (i.e.
-    *           the file can be never persisted into the file system, the name is used to map some
-    *           ID to the AST).
-    * @param    resolver
-    *           The current instance of the {@link AstSymbolResolver} in use
-    *           or <code>null</code>.
-    *
-    * @return   The root AST that was created or <code>null</code> on error.
-    */
-   public static JavaAst createRootAst(String filename, AstSymbolResolver resolver)
-   {
-      JavaAst result = createAst(COMPILE_UNIT,
-                                 "compilation unit",
-                                 null,
-                                 null,
-                                 -1);
-      initializeRootAst(result, filename, resolver);
-
-      return result;
-   }
-   
-   /**
     * Generate a unique Java AST persistence filename based on the passed
-    * filename.
-    *
-    * @param    filename
-    *           The filename used as a base for the Java AST filename.
-    *
-    * @return   A unique filename based on the source filename with '.jast'
+    * artifact.
+    *
+    * @param    artifact
+    *           The artifact used as a base for the Java AST filename.
+    *
+    * @return   A unique artifact based on the source artifact with '.jast'
     *           appended.
     */
-   public static String generateJavaFilename(String filename)
+   public static Artifact generateJavaArtifact(Artifact artifact)
    {
-      if (filename.endsWith(JAST_POSTFIX))
+      if (artifact.endsWith(JAST_POSTFIX))
       {
-         return filename;
+         return artifact;
       }
       
-      StringBuilder sb = new StringBuilder(filename);
+      StringBuilder sb = new StringBuilder(artifact.getRelativePath());
       
-      return sb.append(JAST_POSTFIX).toString();
+      return ArtifactManager.addArtifact(sb.append(JAST_POSTFIX).toString());
    }
    
    /**
     * Clears past contents of the new files list and enables tracking mode
     * where all JAST files that are persisted are logged in a set that
-    * can be queried by {@link #getPersistedFiles}.  Use 
+    * can be queried by {@link #getPersistedArtifacts()}.  Use
     * {@link #disableNewFileTracking} when the tracking session is no
     * longer needed.
     */
    public static void enableNewFileTracking()
    {
       WorkArea wa = context.get();
-      wa.pfiles = new HashSet<>();
+      wa.pfiles = new ArtifactCollection();
       wa.track = true;
    }
    
    /**
     * Disables tracking mode such that any JAST files that are persisted
     * will no longer be logged in the set that can be queried by
-    * {@link #getPersistedFiles}.  Does not alter the contents of the current
+    * {@link #getPersistedArtifacts()}.  Does not alter the contents of the current
     * persisted files list.  Use this to finish a session started with 
     * {@link #enableNewFileTracking}
     */
@@ -322,15 +304,15 @@
    }
    
    /**
-    * Accesses the list of files persisted since the last call
+    * Accesses the collection of artifacts with files persisted since the last call
     * to {@link #enableNewFileTracking}.  There are no duplicate entries in
     * the list.
     *
-    * @return   The list of files that have been recently persisted.
+    * @return   The collection of artifacts with files that have been recently persisted.
     */
-   public static String[] getPersistedFiles()
+   public static ArtifactCollection getPersistedArtifacts()
    {
-      return (String[]) context.get().pfiles.toArray(new String[0]);
+      return context.get().pfiles;
    }
    
    /**
@@ -386,19 +368,18 @@
       
       if (wa.autoload)
       {
-         String filename = generateJavaFilename(ast);
-         filename = Configuration.normalizeFilename(filename);
+         Artifact artifact = generateJavaArtifact(ast);
 
          try
          {
-            if (AstManager.get().isExistingAst(filename))
+            if (AstManager.get().isExistingAst(artifact))
             {
-               helper.loadJavaFile(filename, true);
+               helper.loadJavaFile(artifact, true);
             }
             else
             {
                // create a new target tree
-               wa.root = wa.current = createRootAst(new File(filename), getResolver());
+               wa.root = wa.current = createRootAst(artifact, getResolver());
             }            
          }
          
@@ -410,16 +391,16 @@
    }
    
    /**
-    * Generate a unique Java AST persistence filename based on the current
-    * source AST filename.
+    * Generate a unique Java AST persistence artifact based on the current
+    * source AST artifact.
     *
-    * @return   A unique filename based on the source filename with '.jast'
+    * @return   A unique artifact based on the source artifact with '.jast'
     *           appended.
     */
-   private String generateJavaFilename(Aast ast)
+   private Artifact generateJavaArtifact(Aast ast)
    {
-      return generateJavaFilename(ast.getFilename());
-   }     
+      return generateJavaArtifact(ast.getArtifact());
+   }
    
    /**
     * Helper to create, edit, move and delete Java ASTs.
@@ -892,16 +873,16 @@
        * Any changes made during processing of the previous AST will be lost
        * if they haven't been persisted by this point.
        *
-       * @param   filename
-       *          The filename to use as the Java AST filename.
+       * @param   artifact
+       *          The artifact to use as the Java AST filename.
        * @param   projectRelative
-       *          <code>true</code> if the filename is relative to the
-       *          project home directory, <code>false</code> if the filename 
+       *          <code>true</code> if the artifact filename is relative to the
+       *          project home directory, <code>false</code> if the artifact filename
        *          is absolute or relative to the current directory.
        */
-      public boolean createJavaFile(String filename, boolean projectRelative)
+      public boolean createJavaFile(Artifact artifact, boolean projectRelative)
       {
-         return createJavaFile(filename, projectRelative, false);
+         return createJavaFile(artifact, projectRelative, false);
       }
 
       /**
@@ -911,13 +892,13 @@
        * Any changes made during processing of the previous AST will be lost
        * if they haven't been persisted by this point.
        *
-       * @param   filename
-       *          The filename to use as the Java AST filename.
+       * @param   artifact
+       *          The artifact to use as the Java AST filename.
        */
-      public JavaAst createJavaFile(String filename)
+      public JavaAst createJavaFile(Artifact artifact)
       {
          JavaAst result = createAst(COMPILE_UNIT, "compilation unit", null, null, -1);
-         result.brainwash(filename);
+         result.brainwash(artifact);
          
          return result;
       }
@@ -929,8 +910,8 @@
        * Any changes made during processing of the previous AST will be lost
        * if they haven't been persisted by this point.
        *
-       * @param   filename
-       *          The filename to use as the Java AST filename.
+       * @param   artifact
+       *          The artifact to use as the Java AST artifact.
        * @param   projectRelative
        *          <code>true</code> if the filename is relative to the
        *          project home directory, <code>false</code> if the filename 
@@ -939,34 +920,14 @@
        *          If <code>true</code> then the file is not supposed to be persisted into the file
        *          system, the file name is used to map some ID to the AST.
        */
-      public boolean createJavaFile(String filename, boolean projectRelative, boolean virtualFile)
+      public boolean createJavaFile(Artifact artifact, boolean projectRelative, boolean virtualFile)
       {
          WorkArea wa = context.get();
          
          try
          {
-            if (virtualFile)
-            {
-               wa.root = wa.current = createRootAst(filename, getResolver());
-            }
-            else
-            {
-               File file;
-            
-               if (projectRelative)
-               {
-                  file = Configuration.toFile(filename);
-               }
-               else
-               {
-                  file = new File(filename);
-               }
-         
-               // create a new target tree
-               wa.root = wa.current = createRootAst(file, getResolver());
-            }
+            wa.root = wa.current = createRootAst(artifact, getResolver());
          }
-         
          catch (Exception exc)
          {
             // some failure, we just won't have a Java AST!
@@ -983,28 +944,23 @@
        * Any changes made during processing of the previous AST will be lost
        * if they haven't been persisted by this point.
        *
-       * @param   filename
-       *          The filename from which to read the Java AST.
+       * @param   artifact
+       *          The artifact from which to read the Java AST.
        * @param   projectRelative
        *          <code>true</code> if the filename is relative to the
        *          project home directory, <code>false</code> if the filename 
        *          is absolute or relative to the current directory.
        */
-      public boolean loadJavaFile(String filename, boolean projectRelative)
+      public boolean loadJavaFile(Artifact artifact, boolean projectRelative)
       {
          WorkArea wa = context.get();
          
          try
          {
-            if (projectRelative)
-            {
-               filename = Configuration.normalizeFilename(filename);
-            }
-            
-            if (AstManager.get().isExistingAst(filename))
+            if (AstManager.get().isExistingAst(artifact))
             {
                // read the existing target tree
-               wa.root = wa.current = (JavaAst) AstManager.get().loadTree(filename);
+               wa.root = wa.current = (JavaAst) AstManager.get().loadTree(artifact);
                
                // set the current Java target AST into the resolver
                getResolver().setTargetRootAst(wa.root);
@@ -1054,7 +1010,7 @@
              AstException,
              IOException
       {
-         return persistJavaFile(getSource().getFilename());
+         return persistJavaFile(getSource().getArtifact());
       }
       
       /**
@@ -1078,7 +1034,7 @@
              AstException,
              IOException
       {
-         return persistJavaAst(ast.getFilename(), ast);
+         return persistJavaAst(ast.getArtifact(), ast);
       }
 
       /**
@@ -1086,8 +1042,8 @@
        * child).  If this AST is empty, it will be removed from the {@link AstManager} which will
        * essentially disable further use of this AST.
        *
-       * @param    filename
-       *           Base filename (without <code>jast</code> extension) under which to save the 
+       * @param    artifact
+       *           Artifact (without <code>jast</code> extension) under which to save the
        *           Java AST. Must be an absolute path or relative to the project root.
        * @param    ast
        *           The AST to be persisted.
@@ -1100,14 +1056,12 @@
        * @throws   AstException
        *           if there is an error writing Java AST.
        */
-      public boolean persistJavaAst(String filename, JavaAst ast)
+      public boolean persistJavaAst(Artifact artifact, JavaAst ast)
       throws ConfigurationException,
              AstException,
              IOException
       {
          WorkArea wa = context.get();
-         
-         filename = Configuration.normalizeFilename(filename);
 
          // bypass persistence if there is an empty tree
          if (ast.getNumImmediateChildren() == 0)
@@ -1115,7 +1069,7 @@
             try
             {
                // no need to maintain this value in the registry
-               AstManager.get().removeTree(filename);
+               AstManager.get().removeTree(artifact);
             }
             catch (Exception exc)
             {
@@ -1126,12 +1080,12 @@
          }
          
          // read the existing target tree
-         AstManager.get().saveTree(ast, filename, false);
+         AstManager.get().saveTree(ast, artifact, false);
          
          // save this into the list of new files if tracking is enabled
          if (wa.track)
          {
-            wa.pfiles.add(filename);
+            wa.pfiles.add(artifact);
          }
          
          return true;
@@ -1143,8 +1097,8 @@
        * removed from the {@link AstManager} which will essentially disable
        * further use of this AST.
        *
-       * @param    filename
-       *           Base filename (without <code>jast</code> extension) under
+       * @param    artifact
+       *           Artifact (without <code>jast</code> extension) under
        *           which to save the Java AST. Must be an absolute path or
        *           relative to the project root.
        *
@@ -1157,14 +1111,14 @@
        * @throws   AstException
        *           if there is an error writing Java AST.
        */
-      public boolean persistJavaFile(String filename)
+      public boolean persistJavaFile(Artifact artifact)
       throws ConfigurationException,
              AstException,
              IOException
       {
-         filename = generateJavaFilename(filename);
+         artifact = generateJavaArtifact(artifact);
 
-         return persistJavaAst(filename, context.get().root);
+         return persistJavaAst(artifact, context.get().root);
       }
    }
    
@@ -1173,8 +1127,8 @@
     */
    private static class WorkArea
    {
-      /** The list of persisted files between 2 arbitrary points in time. */
-      private Set<String> pfiles = null;
+      /** The collection of artifact with persisted files between 2 arbitrary points in time. */
+      private ArtifactCollection pfiles = null;
       
       /** <code>true</code> if persisted file names must be tracked. */
       private boolean track = false;
@@ -1191,4 +1145,4 @@
       /** Root of the target tree currently being processed. */
       private JavaAst root = null;
    }
-}
\ No newline at end of file
+}

=== modified file 'src/com/goldencode/p2j/uast/LexerDumpFilter.java'
--- old/src/com/goldencode/p2j/uast/LexerDumpFilter.java	2022-10-28 10:54:33 +0000
+++ new/src/com/goldencode/p2j/uast/LexerDumpFilter.java	2025-05-09 07:59:18 +0000
@@ -2,7 +2,7 @@
 ** Module   : LexerDumpFilter.java
 ** Abstract : wraps a lexer token stream, acting as a filter this writes the tokens to file
 **
-** Copyright (c) 2004-2022, Golden Code Development Corporation.
+** Copyright (c) 2004-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ---------------------------Description----------------------------
 ** 001 GES 20050221   @19919 First version, with support for:
@@ -24,7 +24,9 @@
 **                           exception.  We also see this problem at times when there is no
 **                           exception and this may also solve that issue.
 ** 007 TJD 20220504          Java 11 compatibility minor changes
-*/ 
+** 008 DDF 20250407          Added new constructors that use SourceArtifact.
+*/
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -80,6 +82,7 @@
 
 package com.goldencode.p2j.uast;
 
+import com.goldencode.artifacts.SourceArtifact;
 import com.goldencode.util.*;
 import java.io.*;
 import java.text.*;
@@ -186,6 +189,48 @@
    }
    
    /**
+    * Constructs a lexer using an artifact as input and associating a
+    * <code>SymbolResolver</code> with the instance, will open an output
+    * stream using the passed <code>File</code> object.  User must call
+    * {@link #setOutput} before starting to lex, otherwise no output
+    * will be created.
+    *
+    * @param    artifact
+    *           The artifact representing the Progress 4GL source code.
+    * @param    sr
+    *           Provides keyword dictionary storage and lookup for symbol
+    *           resolution in the lexer.
+    */
+   public LexerDumpFilter(SourceArtifact artifact, SymbolResolver sr)
+   throws IllegalArgumentException,
+   IOException
+   {
+      super(artifact, sr);
+      initialize();
+   }
+   
+   /**
+    * Constructs a lexer using an artifact as input and associating a
+    * <code>SymbolResolver</code> with the instance, will open an output
+    * stream using the passed <code>File</code> object.
+    *
+    * @param    artifact
+    *           The artifact representing the Progress 4GL source code.
+    * @param    sr
+    *           Provides keyword dictionary storage and lookup for symbol
+    *           resolution in the lexer.
+    * @param    file
+    *           The output file to which the tokens will be written.
+    */
+   public LexerDumpFilter(SourceArtifact artifact, SymbolResolver sr, File file)
+   throws IllegalArgumentException,
+   IOException
+   {
+      this(artifact, sr);
+      openOutput(file);
+   }
+   
+   /**
     * This is the main filter point which calls the parent lexer, obtains
     * the next token, writes it to the output file and then returns it to
     * the caller.

=== modified file 'src/com/goldencode/p2j/uast/RootNodeList.java'
--- old/src/com/goldencode/p2j/uast/RootNodeList.java	2018-11-28 10:06:31 +0000
+++ new/src/com/goldencode/p2j/uast/RootNodeList.java	2025-05-09 07:59:18 +0000
@@ -2,7 +2,7 @@
 ** Module   : RootNodeList.java
 ** Abstract : maintains a project-specific list of root entry point procedures
 **
-** Copyright (c) 2005-2018, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------Description-----------------------------
 ** 001 GES 20050314   @20312 Created initial version which has support 
@@ -18,7 +18,10 @@
 ** 008 GES 20170531          Fix loading when current directory is not the same as project home.
 ** 009 CA  20170922          Fixed a path compatibility when generating the callgraph on windows.
 ** 010 CA  20181128          Fixed rootlist file calculation when folders are used.
+** 011 GES 20230524          Moved some code to a more generic package.
+**     DDF 20250327          Replaced usage of FileSpecList with ArtifactCollection.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -77,6 +80,8 @@
 import java.io.*;
 import java.util.*;
 import org.w3c.dom.*;
+
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.util.*;
 import com.goldencode.p2j.cfg.*;
@@ -116,8 +121,8 @@
    /** XML filename for persisting the registry. */
    private File file = null;      
       
-   /** Stores the root node list. */
-   private ArrayList<String> nodes = null;
+   /** Stores the root node artifact collection. */
+   private ArtifactCollection nodes = null;
          
    /**
     * Constructor which looks up the root node list filename and initializes
@@ -151,7 +156,9 @@
       
       // try to load the persisted root node list
       if (file.exists())
+      {
          load();
+      }
    }
    
    /**
@@ -163,9 +170,12 @@
    public String[] getList()
    {
       if (nodes == null)
+      {
          return null;
-      
-      return (String[])nodes.toArray(new String[0]);
+      }
+
+      List<String> names = ArtifactManager.getArtifactsRelativePath(nodes);
+      return names.toArray(new String[0]);
    }
    
    /**
@@ -213,7 +223,7 @@
       // leave our list as null, if there are no entries
       if (len > 0)
       {
-         nodes = new ArrayList<>();
+         nodes = new ArtifactCollection();
       }
       
       for (int i = 0; i < len; i++)
@@ -236,14 +246,10 @@
             }
 
             boolean recursive = "true".equalsIgnoreCase(elem.getAttribute(ATTR_RECURSIVE));
-            FileSpecList spec = new FileSpecList(folder, pattern, recursive);
-            
-            for (String filename : spec.listFilenames())
-            {
-               filename = Configuration.normalizeFilename(filename);
-               
-               nodes.add(filename);
-            }
+            nodes = ArtifactManager.createArtifactsByFileList(FileSpecList.class,
+                                                              folder,
+                                                              pattern,
+                                                              recursive);
          }
          else
          {
@@ -253,8 +259,9 @@
                throw new FileNotFoundException("File '" + name + "' does not exist!");
             }
             file = new File(name);
-            nodes.add(file.getPath());
+            Artifact artifact = ArtifactManager.addArtifact(file.getPath());
+            nodes.add(artifact);
          }
       }
    }
-}
\ No newline at end of file
+}

=== modified file 'src/com/goldencode/p2j/uast/ScanDriver.java'
--- old/src/com/goldencode/p2j/uast/ScanDriver.java	2025-04-10 13:01:29 +0000
+++ new/src/com/goldencode/p2j/uast/ScanDriver.java	2025-05-09 07:59:18 +0000
@@ -139,15 +139,16 @@
 
 package com.goldencode.p2j.uast;
 
-import java.io.*;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactCollection;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
-import com.goldencode.p2j.util.*;
 
 /**
  * Provides a simple harness to scan a user-defined list of files and drive
@@ -210,13 +211,13 @@
    private static final ConversionStatus LOG = ConversionStatus.get(ScanDriver.class);
    
    /** Keeps track of the class of the files referenced in a source from conversion list. */
-   private static Set<File> additionalFileList = Collections.synchronizedSet(new HashSet<>());
+   private static Set<Artifact> additionalFileList = Collections.synchronizedSet(new HashSet<>());
    
    /** Keeps track of the class of the files referenced in a source from conversion list. */
-   private static Set<File> removedConversionFiles = Collections.synchronizedSet(new HashSet<>());
+   private static Set<Artifact> removedConversionFiles = Collections.synchronizedSet(new HashSet<>());
    
    /** Iterator over the conversion list. */
-   private static ListIterator<File> iter = null;
+   private static ListIterator<Artifact> iter = null;
    
    static
    {
@@ -238,14 +239,14 @@
     * @param    file
     *           Referenced 4gl source. 
     */
-   public static void addSource(File file)
+   public static void addSource(Artifact artifact)
    {  
-      if (!additionalFileList.contains(file))
+      if (!additionalFileList.contains(artifact))
       {         
-         additionalFileList.add(file);
+         additionalFileList.add(artifact);
          synchronized (iter)
          {
-            iter.add(file);
+            iter.add(artifact);
             iter.previous();            
          }
       }
@@ -257,32 +258,9 @@
     * @param    file
     *           Referenced 4gl source. 
     */
-   public static boolean removeSource(File file)
-   {
-      return removedConversionFiles.add(file);
-   }
-   
-   /**
-    * Drive the scanning process for each of the given files in the list,
-    * with preprocessing activated and caching of all output (preprocessor,
-    * lexer, parser), persistence of ASTs and no registry rebuild.
-    *
-    * @param    files
-    *           The list of files.  Must not be <code>null</code>.
-    * @param    silent
-    *           <code>false</code> if screen (stdout) output should be done.
-    *           If <code>true</code>, normal screen output is suppressed and
-    *           the only output occurs on failures (this is written to
-    *           stderr).
-    * 
-    * @throws   Exception
-    *           in the event <code>abortOnError</code> is true and a failure occurs.
-    */
-   public static void scan(FileList files, boolean silent)
-   throws Exception
-   {
-      // scan with default thread pool
-      scan(files, silent, false, 1);
+   public static boolean removeSource(Artifact artifact)
+   {
+      return removedConversionFiles.add(artifact);
    }
    
    /**
@@ -309,10 +287,10 @@
     * @throws   Exception
     *           in the event <code>abortOnError</code> is true and a failure occurs.
     */
-   public static void scan(FileList files, boolean silent, boolean abortOnError, int threads)
+   public static void scan(ArtifactCollection col, boolean silent, boolean abortOnError, int threads)
    throws Exception
    {
-      scan(files, true, true, true, true, true, silent, abortOnError, threads);
+      scan(col, true, true, true, true, true, silent, abortOnError, threads);
    }
    
    /**
@@ -350,7 +328,7 @@
     * @throws   Exception
     *           in the event <code>abortOnError</code> is true and a failure occurs.
     */
-   public static void scan(FileList files,
+   public static void scan(ArtifactCollection artifacts,
                            boolean  preproc,
                            boolean  cache,
                            boolean  lex,
@@ -361,31 +339,13 @@
                            int      threads)
    throws Exception
    {
-      // setup
-      File[] list = files.list();
-      
-      Set<String> fileCvtList = new HashSet<>();
-        
-      for (int i = 0; i < list.length; i++)
-      {
-         try 
-         {
-            fileCvtList.add(list[i].getCanonicalPath());            
-         }
-         catch (IOException exc)
-         {
-            LOG.log(Level.SEVERE, "", exc);
-         }
-      }
-      
-      SymbolResolver.setCvtSources(fileCvtList);
-      
-      // reorganize files as necessary to minimize redundant HTML processing
-      list = AstGenerator.prioritize(list);
-      
-      // create a thread-safe list of files
-      List<File> safeList = Collections.synchronizedList(new ArrayList<>(Arrays.asList(list)));
-      
+      SymbolResolver.setCvtSources(artifacts);
+      
+      Comparator<Artifact> comparator = Comparator
+               .comparing((Artifact a) -> AstGenerator.isHTML(a.getRelativePath()) ? 0 : 1)
+               .thenComparing(Artifact::getRelativePath);
+      artifacts.sort(comparator);
+
       // use all available processors if a hard thread limit was not specified
       if (threads == 0)
       {
@@ -408,13 +368,13 @@
       Queue<Future<Result>> results = new LinkedList<>();
       
       // process the list of files
-      iter = safeList.listIterator();
+      iter = artifacts.listIterator();
       for (int i = 0; iter.hasNext(); i++)
       {
-         File file = iter.next();
-         
+         Artifact artifact = iter.next();
+
          // bypass entries that have been removed
-         if (file == null)
+         if (artifact == null)
          {
             continue;
          }
@@ -437,16 +397,17 @@
          {
             Result result = new Result();
             
-            File f = safeList.get(index);
-            if (f != null)
+            Artifact a = artifacts.get(index);
+            if (a != null)
             {
-               String fileName = result.fileName = f.toString();
+               String fileName = result.fileName = a.getRelativePath();
                
                config.withFileProfile(fileName);
                
                try
                {
-                  boolean exists = AstManager.get().isExistingAst(AstGenerator.getAstName(fileName));
+                  Artifact astArtifact = ArtifactManager.addArtifact(a.getRelativePath() + ".ast");
+                  boolean exists = AstManager.get().isExistingAst(astArtifact);
                   
                   if (!silent && singleThreaded)
                   {
@@ -463,11 +424,11 @@
                      // since it was already processed fully to an AST when it was
                      // listed as an HTML file, it doesn't need to be processed
                      // again)
-                     gen.processFile(fileName, safeList, index);
+                     gen.processFile(a, artifacts, index);
                   }
                   else
                   {
-                     gen.markOriginal(fileName);
+                     gen.markOriginal(a);
                   }
                }
                catch(Exception exc)
@@ -560,14 +521,14 @@
       
       if (additionalFileList != null && additionalFileList.size() > 0)
       {         
-         files.addAdditionalFiles(additionalFileList);
+         artifacts.addAll(additionalFileList);
          
          additionalFileList = null;
       }
       
       if (removedConversionFiles != null)
       {   
-         files.removeFiles(removedConversionFiles);
+         artifacts.removeAll(removedConversionFiles);
          removedConversionFiles = null;
       }
       
@@ -638,142 +599,4 @@
       
       System.exit(rc);
    }
-   
-   /**
-    * Provides a command line interface for an end user to drive the
-    * scanning harness.
-    * <p>
-    * Syntax:
-    * <pre>
-    *   java ScanDriver [-QCLPXSANR] &lt;directory&gt; &lt;&quot;filespec&quot;&gt;
-    *   java ScanDriver -F[QCLPXSA] &lt;&quot;filelist&quot;&gt;
-    * </pre>
-    * Where:
-    * <ul>
-    *   <li> [-options] can be a hyphen followed by any combination of the
-    *        letters 'QCLPXR' where
-    *   <ul>
-    *      <li> Q = run preprocessor    
-    *      <li> C = cache preprocessor output
-    *      <li> L = cache lexer output
-    *      <li> P = cache parser output
-    *      <li> X = save ASTs persistently
-    *      <li> F = interpret remaining args as an explicit file list
-    *      <li> S = silent mode    
-    *      <li> A = abort on error",
-    *      <li> N = no recursion in filespec mode
-    *      <li> R = registry rebuild (from persistent ASTs)
-    *   </ul>
-    *   <li> filespec mode
-    *   <ul>
-    *      <li> &lt;directory&gt; is the directory to search for files
-    *      <li> &quot;filespec&quot; is the file specification that will be
-    *           used to create a list of files to process
-    *   </ul>
-    *   <li> explicit file list mode
-    *   <ul>
-    *      <li> &lt;filelist&gt; is arbitrary list of absolute and/or
-    *           relative file names to scan
-    *   </ul> 
-    * </ul>
-    * <p>
-    * By default, options QCLPX are active.  If any options are explicitly
-    * specified, all non-specified options are forced off. 
-    *
-    * @param   args 
-    *          List of command line arguments.
-    */
-   public static void main(String[] args)
-   {
-      // option defaults
-      boolean preproc      = true;
-      boolean cache        = true;
-      boolean lex          = true; 
-      boolean parse        = true;
-      boolean persist      = true;
-      boolean recursion    = true;
-      boolean silent       = false;
-      boolean abortOnError = false;
-      boolean explicit     = false;
-      int     idx          = 0;
-      
-      // requires (directory and filespec) OR (option -F and a filelist)
-      if (args.length < 2)
-      {
-         syntax("Missing command line parameters.", -1);
-      }
-      
-      // process options
-      if (args[0].startsWith("-"))
-      {
-         idx = 1;
-         String opts = args[0].toLowerCase();
-         
-         if (opts.indexOf('q') != -1) preproc = true;
-         else preproc = false;
-         
-         if (opts.indexOf('c') != -1) cache = true;
-         else cache = false;
-         
-         if (opts.indexOf('l') != -1) lex = true;
-         else lex = false;
-                  
-         if (opts.indexOf('p') != -1) parse = true;
-         else parse = false;
-         
-         if (opts.indexOf('x') != -1) persist = true;
-         else persist = false;
-         
-         if (opts.indexOf('n') != -1) recursion = false;
-         else recursion = true;
-         
-         if (opts.indexOf('s') != -1) silent = true;
-         else silent = false;
-         
-         if (opts.indexOf('a') != -1) abortOnError = true;
-         else abortOnError = false;
-         
-         if (opts.indexOf('f') != -1) explicit = true;
-         else explicit = false;
-      }
-      
-      FileList files = null;
-      
-      // at this point idx points to the next arg after the options
-      if (explicit)
-      {
-         int      len      = args.length;
-         String[] filelist = new String[len - idx];
-         
-         for (int j = idx; j < len; j++)
-         {
-            filelist[j - 1] = args[j];
-         }
-         
-         files = new ExplicitFileList(filelist);
-      }
-      else
-      {
-         // options with filespec mode requires 3 args in total
-         if (idx == 1 && args.length < 3)
-         {
-            syntax("Missing command line parameters.", -2);
-         }
-         
-         files = new FileSpecList(new File(args[idx]),
-                                  args[idx+1],
-                                  recursion);
-      }
-      
-      // drive the scanning
-      try
-      {
-         scan(files, preproc, cache, lex, parse, persist, silent, abortOnError, 0);
-      }
-      catch (Exception exc)
-      {
-         // non-zero return to indicate error
-         System.exit(1);
-      }
-   }
 }

=== modified file 'src/com/goldencode/p2j/uast/SymbolResolver.java'
--- old/src/com/goldencode/p2j/uast/SymbolResolver.java	2025-04-22 06:57:43 +0000
+++ new/src/com/goldencode/p2j/uast/SymbolResolver.java	2025-05-09 08:09:40 +0000
@@ -607,6 +607,8 @@
 import java.util.function.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.*;
+import com.goldencode.artifacts.FileSpecList;
 import com.goldencode.ast.*;
 import com.goldencode.util.*;
 import antlr.*;
@@ -740,7 +742,7 @@
     * as not all classes might be loaded.
     */
    private final ClassDefinition MOCK_CLASS_DEF = 
-   new ClassDefinition(null, null, null, false, OOType.CLASS, false, "")
+   new ClassDefinition(null, null, null, false, OOType.CLASS, false, null)
    {
       @Override
       public void annotateMethodCall(String         mname,
@@ -903,7 +905,7 @@
    };
    
    /** Cache of resolved classes, per absolute directory path. */
-   private static final Map<String, String[]> DIR_CLASSES = new ConcurrentHashMap<>();
+   private static final Map<String, ArtifactCollection> DIR_CLASSES = new ConcurrentHashMap<>();
    
    /**
     * An exemplar from which the dictionaries will be populated, when creating instances via 
@@ -935,7 +937,7 @@
    }
 
    /** Classes in the propath, mapped qualified classname to filename. */
-   private Map<String, List<String>> propathCls = null;
+   private Map<String, ArtifactCollection> propathCls = null;
 
    /** Stores class and interface definitions. */
    private ScopedSymbolDictionary classDict = null;
@@ -1318,7 +1320,7 @@
       if (qname == null)
       {
          // 'qualified_oo_name' may not have been set yet
-         String fname = ast.getFilename();
+         String fname = ast.getArtifact().getRelativePath();
          if (fname.toLowerCase().endsWith(".cls"))
          {
             try
@@ -1372,7 +1374,7 @@
     * @param    fileList
     *           Set with the names of the sources.
     */
-   public static void setCvtSources (Set<String> sources)
+   public static void setCvtSources(ArtifactCollection sources)
    {
       WorkArea wa = locate();
       if (wa.cvtSources != null)
@@ -1415,29 +1417,29 @@
    }
    
    /**
-    * Obtain the list of all "fakeout" class and interface definitions that
+    * Obtain the collection of artifacts for all "fakeout" class and interface definitions that
     * exist in the project.  These <code>.cls</code> files represent the
     * Progress built-ins and all .NET classes/interfaces that can be accessed.
     * <p>
     * These files will be listed in the directories represented by the
     * {@link #OO4GL_PATH}, {@link #DOTNET_PATH} and the {@link #ASSEMBLY_PATH}.
     *
-    * @return   The list of <code>.cls</code> files, in the same case in which
+    * @return   The collection of artifacts for <code>.cls</code> files, in the same case in which
     *           they exist in the filesystem.
     */
-   public static String[] listFakeoutFiles()
+   public static ArtifactCollection listFakeoutArtifacts()
    {
-      String[] oo4gl    = getClassFileList(oo4glPath, false);
-      String[] dotnet   = getClassFileList(dotnetPath, false);
-      String[] assembly = getClassFileList(ASSEMBLY_PATH, false);
-      
-      ArrayList<String> list = new ArrayList<>();
-      
-      list.addAll(Arrays.asList(oo4gl));
-      list.addAll(Arrays.asList(dotnet));
-      list.addAll(Arrays.asList(assembly));
-      
-      return list.toArray(new String[0]);
+      ArtifactCollection oo4gl    = getClassFileList(oo4glPath, false);
+      ArtifactCollection dotnet   = getClassFileList(dotnetPath, false);
+      ArtifactCollection assembly = getClassFileList(ASSEMBLY_PATH, false);
+      
+      List<ArtifactCollection> list = new ArrayList<>();
+      
+      list.add(oo4gl);
+      list.add(dotnet);
+      list.add(assembly);
+      
+      return ArtifactManager.mergeArtifactCollections(list);
    }
    
    /**
@@ -2447,16 +2449,16 @@
     *           <code>true</code> if the legacy system had a case-sensitive
     *           file-system.
     *
-    * @return   The list of files that exist.  If there are no files, the
-    *           array will have a 0 length.
+    * @return   The collection of artifacts for files that exist.  If there are no files, the
+    *           collection will be empty.
     */
-   public static String[] getClassFileList(String path, boolean caseSens)
+   public static ArtifactCollection getClassFileList(String path, boolean caseSens)
    {
       File dir = new File(path);
       
       if (!dir.exists() || !dir.isDirectory())
       {
-         return new String[0];
+         return new ArtifactCollection();
       }
       
       String absPath = null;
@@ -2470,22 +2472,18 @@
          LOG.log(Level.SEVERE, "", exc);
       }
       
-      String[] files = DIR_CLASSES.get(absPath);
-      if (files == null)
+      ArtifactCollection artifacts = DIR_CLASSES.get(absPath);
+      if (artifacts == null)
       {
-         FileList fl = new FileSpecList(dir, CLASS_SPLAT, true, caseSens);
-         
-         files = fl.listFilenames();
-         for (int i = 0; i < files.length; i++)
-         {
-            String l = files[i];
-            l = Configuration.normalizeFilename(l);
-            files[i] = l;
-         }
-         DIR_CLASSES.put(absPath, files);
+         artifacts = ArtifactManager.createArtifactsByFileList(FileSpecList.class,
+                                                               dir,
+                                                               CLASS_SPLAT,
+                                                               true,
+                                                               caseSens);
+         DIR_CLASSES.put(absPath, artifacts);
       }
       
-      return files;
+      return artifacts;
    }
    
    /**
@@ -2495,7 +2493,7 @@
     *
     * @param    map
     *           The map to modify.
-    * @param    list
+    * @param    col
     *           The elements to add.
     * @param    path
     *           The root path in which all elements reside.
@@ -2503,11 +2501,16 @@
     *           <code>true</code> if the legacy system had a case-sensitive
     *           file-system.
     */
-   public static void createClassMappings(Map<String, List<String>> map,
-                                          String[]                  list,
-                                          String                    path,
-                                          boolean                   caseSens)
+   public static void createClassMappings(Map<String, ArtifactCollection> map,
+                                          ArtifactCollection              col,
+                                          String                          path,
+                                          boolean                         caseSens)
    {
+      if (col == null || col.isEmpty())
+      {
+         return;
+      }
+      
       path = Configuration.normalizeFilename(path);
       int begin = path.length();
       if (path.equals("."))
@@ -2520,23 +2523,34 @@
          begin++;
       }
       
-      for (int i = 0; i < list.length; i++)
+      Iterator<Artifact> iter = col.iterator();
+      while (iter.hasNext())
       {
-         String l = list[i];
-         int end = l.length() - 4;
-         String entry = l.substring(begin, end);
+         Artifact a = iter.next();
+         String relativeName = a.getRelativePath();
+         int end = relativeName.length() - 4;
+         String entry;
+         if (relativeName.startsWith(path))
+         {
+            entry = relativeName.substring(begin, end);
+         }
+         else
+         {
+            // it doesn't start with path, so we just remove "./"
+            entry = relativeName.substring(begin - path.length() + 1, end);
+         }
 
          // only the first entry is kept, in the propath - the others will not be seen
          String key = caseSens ? entry : entry.toLowerCase();
 
-         List<String> propathList = map.get(key);
+         ArtifactCollection propathList = map.get(key);
          if (propathList == null)
          {
-            propathList = new ArrayList<>();
+            propathList = new ArtifactCollection();
             map.put(key, propathList);
          }
             
-         propathList.add(l);
+         propathList.add(a);
       }
    }
    
@@ -3031,18 +3045,18 @@
     * @param    srcFile
     *           Name of the 4GL source code file containing the schema
     *           definitions being persisted.
-    * @param    schemaFile
-    *           Name of schema persistence file.
+    * @param    schemaArtifact
+    *           Artifact of the schema persistence file.
     *
     * @throws   AstException
     *           if any error occurs writing persistence file.
     */
-   public void persistSchemaData(String srcFile, String schemaFile)
+   public void persistSchemaData(Artifact srcFile, Artifact schemaArtifact)
    throws AstException
    {
       if (schemaDict != null)
       {
-         schemaDict.persist(srcFile, schemaFile);
+         schemaDict.persist(srcFile, schemaArtifact);
       }
    }
    
@@ -4224,10 +4238,10 @@
     * @param    imple
     *           Node which has the list of interfaces that are implemented
     *           or <code>null</code> if none exist.
-    * @param    file
-    *           The filename of the .cls that defined this class.
+    * @param    artifact
+    *           The artifact of the .cls that defined this class.
     */
-   public void addClass(Aast symbol, Aast inherits, Aast imple, String file)
+   public void addClass(Aast symbol, Aast inherits, Aast imple, Artifact artifact)
    {
       String name = symbol.getText();
       
@@ -4246,7 +4260,7 @@
          ifaces = calculateParents(name, imple, found.dotnet);         
       }
 
-      addClass(name, parents, ifaces, found.builtin, found.dotnet, file);
+      addClass(name, parents, ifaces, found.builtin, found.dotnet, artifact);
    }
    
    /**
@@ -4266,15 +4280,15 @@
     *           <code>true</code> if this definition is built-in to Progress.
     * @param    dotnet
     *           <code>true</code> if this definition is a .NET class.
-    * @param    file
-    *           The filename of the .cls that defined this class.
+    * @param    artifact
+    *           The artifact of the .cls that defined this class.
     */
    public void addClass(String            name,
                         ClassDefinition[] parents,
                         ClassDefinition[] ifaces,
                         boolean           builtin,
                         boolean           dotnet,
-                        String            file)
+                        Artifact          artifact)
    {
       WorkArea wa = locate();
 
@@ -4283,12 +4297,12 @@
       
       if (!loaded)
       {
-         cls = new ClassDefinition(name, parents, ifaces, builtin, OOType.CLASS, dotnet, file);
+         cls = new ClassDefinition(name, parents, ifaces, builtin, OOType.CLASS, dotnet, artifact);
       }
       
       while (parents != null)
       {
-         SchemaWorker.loadFromPersistence(schemaDict, parents[0].getFilename());
+         SchemaWorker.loadFromPersistence(schemaDict, parents[0].getArtifact());
          
          // classes can only inherit from 1 parent and it must also be a class
          // so we can hard code to the first array element
@@ -4322,17 +4336,17 @@
     *           name.
     * @param    inherits
     *           Optional inherits clause.
-    * @param    file
-    *           The filename of the .cls that defined this interface.
+    * @param    artifact
+    *           The artifact of the .cls that defined this interface.
     */
-   public void addInterface(Aast symbol, Aast inherits, String file)
+   public void addInterface(Aast symbol, Aast inherits, Artifact artifact)
    {
       String          name   = symbol.getText();
       SearchResult    found  = resolveClassName(name);
       // all interfaces inherit methods from Progress.Lang.Object
       ClassDefinition[] parents = calculateParents(name, inherits, found.dotnet);
       
-      addInterface(name, parents, found.builtin, found.dotnet, file);
+      addInterface(name, parents, found.builtin, found.dotnet, artifact);
    }
    
    /**
@@ -4349,14 +4363,14 @@
     *           <code>true</code> if this definition is built-in to Progress.
     * @param    dotnet
     *           <code>true</code> if this definition is a .NET class.
-    * @param    file
-    *           The filename of the .cls that defined this interface.
+    * @param    artifact
+    *           The artifact of the .cls that defined this interface.
     */
    public void addInterface(String            name, 
                             ClassDefinition[] parents,
                             boolean           builtin,
                             boolean           dotnet,
-                            String            file)
+                            Artifact          artifact)
    {
       WorkArea wa = locate();
       if (wa.preScanPass == 0)
@@ -4375,7 +4389,7 @@
                                      builtin,
                                      OOType.INTERFACE,
                                      dotnet,
-                                     file);
+                                     artifact);
       }
       
       // remember this is the interface definition being updated (with
@@ -4400,10 +4414,10 @@
     *           name.
     * @param    flags
     *           {@code true} if this is a flags enum.
-    * @param    file
-    *           The filename of the .cls that defined this interface.
+    * @param    artifact
+    *           The artifact of the .cls that defined this interface.
     */
-   public void addEnum(Aast symbol, boolean flags, String file)
+   public void addEnum(Aast symbol, boolean flags, Artifact artifact)
    {
       String          name   = symbol.getText();
       SearchResult    found  = resolveClassName(name);
@@ -4450,7 +4464,7 @@
                                     found.builtin,
                                     OOType.ENUM,
                                     found.dotnet,
-                                    file);
+                                    artifact);
          
          // the 4GL has an undocumented member of all .NET enum instances that returns the integer value of
          // that instance; this implicit member does NOT exist for 4GL enums; references will appear in the
@@ -4723,7 +4737,7 @@
          // not loaded yet
 
          // do we have a cached file name?
-         if (found.filename == null)
+         if (found.artifact == null)
          {
             // find the class file name in the propath
             if (!srchPropathFindFile(found.clsname, found.type, found))
@@ -4762,8 +4776,6 @@
             generator.setAstPersist(true);
          }
 
-         String relative = Configuration.normalizeFilename(found.filename);
-         
          if (wa.preScanPass == 0)
          {
             wa.preScanRefs.clear();
@@ -4783,11 +4795,11 @@
                System.out.printf("   Lvl%02d parse:%s%s\n", 
                                  wa.preScanPass,
                                  str,
-                                 relative);
+                                 found.artifact.getRelativePath());
             }
 
             // the PROPATH is inherited from the caller
-            generator.preScanClass(relative, found.builtin, found.dotnet, this.propath);
+            generator.preScanClass(found.artifact, found.builtin, found.dotnet, this.propath);
             
             // save all referenced classes/interfaces except for the genesis object
             if (wa.preScanPass > 1)
@@ -4829,7 +4841,7 @@
             if (wa.preScanPass == 0)
             {
                wa.preScanRefs.clear();
-               System.out.println("   Lvl01 DONE:  " + relative);
+               System.out.println("   Lvl01 DONE:  " + found.artifact.getRelativePath());
             }
          }
 
@@ -4840,7 +4852,7 @@
             if (cls == null)
             {
                String spec = "Cannot load class/interface %s from file %s!";
-               String err  = String.format(spec, found.clsname, relative);
+               String err  = String.format(spec, found.clsname, found.artifact.getRelativePath());
                throw new RuntimeException(err);
             }
             
@@ -5043,7 +5055,14 @@
       }
       
       node.putAnnotation("qualified", cls.isJava() ? qname : qname.toLowerCase());
-      node.putAnnotation("source-file", cls.getFilename());
+      if (cls.getArtifact() != null)
+      {
+         node.putAnnotation("source-file", cls.getArtifact().getRelativePath());
+      }
+      else
+      {
+         node.putAnnotation("source-file", cls.getName());
+      }
       node.putAnnotation("builtin-cls", cls.isBuiltIn());
       node.putAnnotation("dotnet-cls", cls.isDotNet());
       node.putAnnotation("indirect-dotnet", cls.isIndirectDotNetReference());
@@ -5303,8 +5322,8 @@
     * used to match unqualified class names with their fully qualified
     * equivalents.
     *
-    * @param   filename
-    *          The file where this USING statement appears.
+    * @param   artifact
+    *          The artifact where this USING statement appears.
     * @param   using
     *          The USING node.
     * @param   name
@@ -5315,7 +5334,7 @@
     *          clause or <code>null</code> if it wasn't present.  A Java package will
     *          only be processed if explicitly specified (non-null FROM JAVA clause).
     */
-   public void addPackage(String filename, Aast using, String name, Aast fromClause) 
+   public void addPackage(Artifact artifact, Aast using, String name, Aast fromClause)
    {
       UsingType type = UsingType.UNSPECIFIED;
       
@@ -5409,14 +5428,14 @@
          // if we are not 'using' the same class as the current class being defined
          SearchResult found = resolveClassName(name, type);
          
-         String relative = (found == null || found.filename == null || found.java) 
+         String relative = (found == null || found.artifact == null || found.java)
                               ? null
-                              : Configuration.normalizeFilename(found.filename);
+                              : Configuration.normalizeFilename(found.artifact.getRelativePath());
                               
          WorkArea wa = locate();
          
          // take a peek at the preScanRefs searching a class we are already parsing
-         if ((relative == null || !filename.equalsIgnoreCase(relative)) && 
+         if ((relative == null || !artifact.getRelativePath().equalsIgnoreCase(relative)) &&
              !(wa.preScanRefs.contains(found.clsname)))
          {
             if (name == null)
@@ -5431,7 +5450,7 @@
          else
          {
             using.putAnnotation("qualified", found.java ? name : name.toLowerCase());
-            using.putAnnotation("source-file", filename);
+            using.putAnnotation("source-file", artifact.getRelativePath());
             using.putAnnotation("builtin-cls", found.builtin);
             using.putAnnotation("dotnet-cls", found.dotnet);
             using.putAnnotation("is-java", found.java);            
@@ -8940,11 +8959,11 @@
       
       cdef.setProcessed(true);
       
-      String file = cdef.getFilename();
+      Artifact artifact = cdef.getArtifact();
       
-      if (!cdef.isBuiltIn() && new File(file).exists())
+      if (!cdef.isBuiltIn() && artifact.exists())
       {
-         generator.processFile(file, null, -1);
+         generator.processFile(artifact, null, -1);
          
          // at this point we have been fully parsed and all parents have been fully parsed, so
          // it is safe to detect whether or not one of our ancestors is a .NET class 
@@ -9113,7 +9132,7 @@
     * will be lowercased.
     *
     * @param    propathCls
-    *           The map to store the classes.
+    *           The map to store the artifacts for classes.
     * @param    propath
     *           The propath to scan for initialization. The propath can change
     *           with each instantiation of this class.  If it changes then
@@ -9123,9 +9142,9 @@
     *           <code>true</code> if the legacy system had a case-sensitive
     *           file-system.
     */
-   private static synchronized ScopedSymbolDictionary initPossibleClasses(Map<String, List<String>> propathCls,
-                                                                          String[]                  propath, 
-                                                                          boolean                   caseSens)
+   private static synchronized ScopedSymbolDictionary initPossibleClasses(Map<String, ArtifactCollection> propathCls,
+                                                                          String[]                        propath, 
+                                                                          boolean                         caseSens)
    {
       String propathKey = "";
       
@@ -9181,7 +9200,7 @@
       if (wa.oo4glCls == null)
       {
          // 4GL classes are matched case-insensitively (even on case-sensitive file systems)
-         wa.oo4glCls = new HashMap<String, List<String>>();
+         wa.oo4glCls = new HashMap<String, ArtifactCollection>();
          createClassMappings(wa.oo4glCls,
                              getClassFileList(oo4glPath, false),
                              oo4glPath,
@@ -9189,7 +9208,7 @@
       }
       if (wa.dotnetCls == null)
       {
-         wa.dotnetCls = new HashMap<String, List<String>>();
+         wa.dotnetCls = new HashMap<String, ArtifactCollection>();
          createClassMappings(wa.dotnetCls,
                              getClassFileList(dotnetPath, caseSens),
                              dotnetPath,
@@ -9197,7 +9216,7 @@
       }
       if (wa.assemblyCls == null)
       {
-         wa.assemblyCls = new HashMap<String, List<String>>();
+         wa.assemblyCls = new HashMap<String, ArtifactCollection>();
          createClassMappings(wa.assemblyCls,
                              getClassFileList(ASSEMBLY_PATH, caseSens), 
                              ASSEMBLY_PATH,
@@ -9693,7 +9712,7 @@
       
       name = convertSeparator(name);
       
-      Map<String, List<String>>[] searchList = null;
+      Map<String, ArtifactCollection>[] searchList = null;
       
       boolean[] builtin   = new boolean[4];
       boolean[] dotnet    = new boolean[4];
@@ -9744,13 +9763,20 @@
       }
       
       WorkArea wa = locate();
-      Set<String> cvtSources = wa.cvtSources;
+      ArtifactCollection cvtSources = wa.cvtSources;
       for (int i = 0; i < searchList.length; i++)
       {
-         Map<String, List<String>> map  = searchList[i];
+         Map<String, ArtifactCollection> map  = searchList[i];
+         // there is a scenario where the map has no elements, so we can skip it
+         if (map.isEmpty())
+         {
+             continue;
+         }
+         
          String                    srch = honorCase[i] ? name : name.toLowerCase();
          
-         List<String> propathList = map.get(srch);
+         // check if there is an artifact available for the processed name
+         ArtifactCollection propathList = map.get(srch);
          if (propathList == null)
          {
             // also try the other separator (Windows on Linux or Linux on Windows), this allows
@@ -9766,19 +9792,18 @@
                // Class previous found in oo4glCls, but also previous
                // found in propath. In this case it should be removed
                // from conversion.
-               if (builtin[i] && !dotnet[i] && result.filename != null)
+               if (builtin[i] && !dotnet[i] && result.artifact != null)
                {
-                  File referenceFile = new File(result.filename);
-                  if (ScanDriver.removeSource(referenceFile))
+                  if (ScanDriver.removeSource(result.artifact))
                   {                     
                      LOG.log(Level.WARNING, 
                              String.format("Legacy builtin class %s found in conversion list, but is part of "
-                                         + "FWD skeletons - this file will be removed from conversion.", srch));
+                                           + "FWD skeletons - this file will be removed from conversion.", srch));
                   }
                   
                }
                
-               result.filename = propathList.get(0);
+               result.artifact = propathList.get(0);
                result.builtin = builtin[i];
                result.dotnet  = dotnet[i];
                
@@ -9794,11 +9819,13 @@
             // present in the conversion list.
             if (!builtin[i] && !dotnet[i] && cvtSources != null)
             {
-               for (String filename: propathList)
+               Iterator<Artifact> iter = propathList.iterator();
+               while (iter.hasNext())
                {
-                  if (cvtSources.contains(filename))
+                  Artifact a = iter.next();
+                  if (cvtSources.contains(a))
                   {
-                     result.filename = filename;
+                     result.artifact = a;
                      result.builtin  = builtin[i];
                      result.dotnet   = dotnet[i];
                      break;
@@ -9807,7 +9834,7 @@
                
                // Log a warning that we could not the filename from multiple
                // propath entries.
-               if (result.filename == null)
+               if (result.artifact == null)
                {
                   LOG.log(Level.WARNING, 
                           String.format("Multiple entries found in PROPATH for %s, but none is in conversion list. "
@@ -9818,17 +9845,16 @@
       }
 
       
-      if (result.filename != null)
+      if (result.artifact != null)
       {
          // Source found in propath, should be added to the conversion list. 
-         if (!result.builtin && !result.dotnet && !cvtSources.contains(result.filename))
+         if (!result.builtin && !result.dotnet && !cvtSources.contains(result.artifact))
          {
             
             String srch = name.replace(File.separatorChar, '.');
             if (loadClassDefinition(this, srch) == null)
             {
-               File referenceFile = new File(result.filename);
-               ScanDriver.addSource(referenceFile);               
+               ScanDriver.addSource(result.artifact);               
             }
          }
          
@@ -10325,8 +10351,8 @@
     */
    private static class SearchResult
    {
-      /** File system object that was found (includes pathing). */
-      private String filename = null;
+      /** Artifact object that was found (includes pathing). */
+      private Artifact artifact = null;
       
       /** Fully qualified class or interface name relative to PROPATH. */
       private String clsname = null;
@@ -10378,16 +10404,16 @@
       private Map<String, ScopedSymbolDictionary> propathClassDicts = new HashMap<>();
       
       /** 4GL sources defined in the conversion list */
-      private Set<String> cvtSources = null;
+      private ArtifactCollection cvtSources = null;
       
       /** Classes in the OO4GL_PATH, mapped qualified classname to filename. */
-      private Map<String, List<String>> oo4glCls = null;
+      private Map<String, ArtifactCollection> oo4glCls = null;
       
       /** Classes in the DOTNET_PATH, mapped qualified classname to filename. */
-      private Map<String, List<String>> dotnetCls = null;
+      private Map<String, ArtifactCollection> dotnetCls = null;
       
       /** Classes in the ASSEMBLY_PATH, mapped qualified classname to filename. */
-      private Map<String, List<String>> assemblyCls = null;
+      private Map<String, ArtifactCollection> assemblyCls = null;
       
       /**
        * The nesting level of {@link SymbolResolver#loadClass} calls when performing 1st-level 

=== modified file 'src/com/goldencode/p2j/uast/UastHintsWorker.java'
--- old/src/com/goldencode/p2j/uast/UastHintsWorker.java	2023-05-15 20:03:25 +0000
+++ new/src/com/goldencode/p2j/uast/UastHintsWorker.java	2025-05-09 07:59:18 +0000
@@ -2,7 +2,7 @@
 ** Module   : UastHintsWorker.java
 ** Abstract : provides a pattern engine service to lookup generic UAST hints
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
 ** 001 GES 20050314   @20326 Created initial version that provides
@@ -45,6 +45,8 @@
 ** 017 VVT 20230318          Unit test configuration support. See #6237.
 ** 018 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
 ** 019 SVL 20230516          Support for dirty-intra-read annotation.
+** 020 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250423          getImportFile() returns an artifact.
 */
 
 /*
@@ -106,6 +108,7 @@
 import java.util.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.cfg.*;
 import com.goldencode.p2j.convert.*;
@@ -214,12 +217,12 @@
          {
             String database = ename.getDatabase();
             SchemaConfig config = Configuration.getSchemaConfig();
-            File importFile = config.getImportFile(database);
-            if (importFile == null)
+            Artifact importArtifact = config.getImportFile(database);
+            if (importArtifact == null)
             {
                return null;
             }
-            extHints = getUastHints(importFile.getAbsolutePath());
+            extHints = getUastHints(importArtifact.getAbsolutePath());
          }
          catch (Exception exc)
          {
@@ -574,13 +577,21 @@
                Aast src  = getResolver().getSourceAst();
                // locate the original file (.df, .p, .w, etc)
                String filename = (String) src.getAncestor(-1).getAnnotation("srcfile");
+               Artifact artifact = null;
                if (filename == null)
                {
                   // if that fails (expected annotation not present), locate the intermediary/artifact file
-                  filename = src.getFilename();
+                  artifact = src.getArtifact();
                }
                
-               hints = getUastHints(Configuration.toFile(filename).getAbsolutePath());
+               if (artifact == null)
+               {
+                  hints = getUastHints(Configuration.toFile(filename).getAbsolutePath());
+               }
+               else
+               {
+                  hints = getUastHints(artifact.getAbsolutePath());
+               }
             }
             catch (Exception exc)
             {

=== modified file 'src/com/goldencode/p2j/uast/Variable.java'
--- old/src/com/goldencode/p2j/uast/Variable.java	2023-10-04 17:40:00 +0000
+++ new/src/com/goldencode/p2j/uast/Variable.java	2025-05-09 07:59:18 +0000
@@ -2,7 +2,7 @@
 ** Module   : Variable.java
 ** Abstract : contains the data needed to define a Progress variable
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------------------------Description----------------------------------
 ** 001 GES 20050210   @19752 First version, simple bean-like approach.
@@ -111,6 +111,7 @@
 ** 040 OM  20230115          Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
 **                           versions, based on node types rather on string paths.
 ** 041 CA  20231004          Added a getter for the 'def' AST node (to be used in read-only mode).
+** 042 DDF 20250408          Replaced usage of filename with artifact.
 */
 
 /*
@@ -978,7 +979,7 @@
             ast.putAnnotation("tempidx", Long.valueOf(tempIdx));
             if (clsDef != null)
             {
-               ast.putAnnotation("tempidx-file", clsDef.getFilename());
+               ast.putAnnotation("tempidx-file", clsDef.getArtifact().getRelativePath());
             }
          }
          
@@ -1020,13 +1021,13 @@
          if (!original && clsDef != null)
          {
             ast.putAnnotation("found-in-cls", clsDef.getName());
-            ast.putAnnotation("found-in-source-file", clsDef.getFilename());
+            ast.putAnnotation("found-in-source-file", clsDef.getArtifact().getRelativePath());
          }
          
          if (isProp)
          {
             ast.putAnnotation("cls-property", true);
-            ast.putAnnotation("cls-container", clsDef.getFilename());
+            ast.putAnnotation("cls-container", clsDef.getArtifact().getRelativePath());
          }
 
          if (original                          &&

=== modified file 'src/com/goldencode/p2j/uast/progress.g'
--- old/src/com/goldencode/p2j/uast/progress.g	2025-04-16 13:47:50 +0000
+++ new/src/com/goldencode/p2j/uast/progress.g	2025-05-09 07:59:18 +0000
@@ -2413,6 +2413,8 @@
 **                           after the reference was resolved.
 ** 428 AL2 20250416          Added a FWD specific query attribute "FORCE-DATABASE-JOIN" that will enforce
 **                           database joining in multi-table queries, bypassing FWD algorithm.
+** 429 DDF 20250407          Added ProgressLexer and ProgressParser constructors that use artifacts.
+**     DDF 20250408          Replaced usage of getFilename() with getArtifact() where possible.
 */
 
 /*
@@ -2489,6 +2491,7 @@
 import java.util.*;
 import java.util.function.*;
 import java.util.logging.*;
+import com.goldencode.artifacts.*;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.schema.*;
 import com.goldencode.util.*;
@@ -5909,6 +5912,9 @@
    /** Honor backslashes as escape characters. */
    private boolean unixEscapes = true;
 
+   /** The associated artifact. */
+   private SourceArtifact artifact = null;
+
    /**
     * <code>true</code> if fields should be scoped to the default buffer. This is used in <code>FORM</code>
     * and <code>DEFINE FRAME</code> for unqualified lookups of fields.
@@ -5942,6 +5948,7 @@
       
       // setup our resolver
       sym = sr;
+      artifact = ((ProgressLexer) lexer).getArtifact();
       initializeProcedureDictionary();
       initializeStreamDictionary();
       initializeFunctionDictionary();
@@ -5953,6 +5960,33 @@
       // set the AST class
       astFactory.setASTNodeClass(AST_CLASS);      
    }
+
+   /**
+    * Gets the filename of the artifact. If there is no artifact available, it will
+    * default to the original method.
+    *
+    * @return  The filename of the artifact.
+    */
+   @Override
+   public String getFilename()
+   {
+      if (artifact != null)
+      {
+         return artifact.getRelativePath();
+      }
+
+      return super.getFilename();
+   }
+
+   /**
+    * Used to retrieve the artifact.
+    *
+    * @return  The artifact.
+    */
+   public SourceArtifact getArtifact()
+   {
+      return artifact;
+   }
    
    /**
     * Writes error data to logger, including a full stack trace.
@@ -11449,7 +11483,8 @@
          SymbolResolver  sym    = new SymbolResolver(true);
          FileReader      fr     = new FileReader(fname);
          BufferedReader  br     = new BufferedReader(fr);
-         ProgressLexer   lexer  = new ProgressLexer(br, sym);
+         SourceArtifact artifact = ArtifactManager.addLexerArtifact(fname, br);
+         ProgressLexer   lexer  = new ProgressLexer(artifact, sym);
          ProgressParser  parser = new ProgressParser(lexer, sym);
          
          // parse the input file and create a tree
@@ -12565,7 +12600,7 @@
          block_term
       )
       {
-         sym.addClass(#s, #inher, #impl, getFilename());
+         sym.addClass(#s, #inher, #impl, getArtifact());
          
          builtInCls = sym.isBuiltInClass(#s.getText()); 
          dotNetCls = sym.isDotNetClass(#s.getText()); 
@@ -12706,7 +12741,7 @@
          (inher:multiple_inherits_clause)? block_term
       )
       {
-         sym.addInterface(#s, #inher, getFilename()); 
+         sym.addInterface(#s, #inher, getArtifact());
          
          builtInCls = sym.isBuiltInClass(#s.getText()); 
          dotNetCls = sym.isDotNetClass(#s.getText()); 
@@ -12809,7 +12844,7 @@
          (f:KW_FLAGS)? block_term
       )
       {
-         sym.addEnum(#s, (#f != null), getFilename()); 
+         sym.addEnum(#s, (#f != null), getArtifact());
          
          builtInCls = sym.isBuiltInClass(#s.getText()); 
          dotNetCls = sym.isDotNetClass(#s.getText()); 
@@ -13039,7 +13074,7 @@
             }
          }
          
-         sym.addPackage(getFilename(), #u, sb.toString(), #f);
+         sym.addPackage(getArtifact(), #u, sb.toString(), #f);
          
          if (preScanPass == 0)
          {
@@ -34799,6 +34834,9 @@
     */
    private SymbolResolver sym = null;
 
+   /** The associated artifact. */
+   private SourceArtifact artifact = null;
+
    /**
     * Creates a <code>Keyword</code> array and initializes that array to
     * the list of all recognized Progress 4GL language keywords. The
@@ -37446,6 +37484,44 @@
       initializeKeywordDictionary();
       readCfg();
    }
+
+   /**
+    * Constructs a lexer using an artifact as input and associating a
+    * <code>SymbolResolver</code> with the instance. The artifact reader will be
+    * wrapped in a {@link DotKludgeReader} instance.
+    *
+    * @param    ar
+    *           The artifact representing the Progress 4GL source code.
+    * @param    sr
+    *           Provides keyword dictionary storage and lookup for symbol
+    *           resolution in the lexer.
+    */
+   public ProgressLexer(SourceArtifact ar, SymbolResolver sr)
+   {
+      this(ar, sr, true);
+   }
+
+   /**
+    * Constructs a lexer using an artifact as input and associating a
+    * <code>SymbolResolver</code> with the instance.
+    *
+    * @param    ar
+    *           The artifact representing the Progress 4GL source code.
+    * @param    sr
+    *           Provides keyword dictionary storage and lookup for symbol
+    *           resolution in the lexer.
+    * @param    kludge
+    *           <code>true</code> to force the artifact reader to be wrapped in a
+    *           {@link DotKludgeReader} instance.
+    */
+   public ProgressLexer(SourceArtifact ar, SymbolResolver sr, boolean kludge)
+   {
+      this(new CharBuffer(kludge ? new DotKludgeReader(ar.getReader()) : ar.getReader()));
+      artifact = ar;
+      sym = sr;
+      initializeKeywordDictionary();
+      readCfg();
+   }
    
    /**
     * Constructs a lexer using stream as input and associating a 
@@ -37458,7 +37534,7 @@
     *           Provides keyword dictionary storage and lookup for symbol
     *           resolution in the lexer.
     */
-   public ProgressLexer(InputStream in, SymbolResolver sr) 
+   public ProgressLexer(InputStream in, SymbolResolver sr)
    {
       this(in, sr, true);
    }
@@ -37476,13 +37552,40 @@
     *           <code>true</code> to force the stream to be wrapped in a
     *           {@link DotKludgeStream} instance.
     */
-   public ProgressLexer(InputStream in, SymbolResolver sr, boolean kludge) 
+   public ProgressLexer(InputStream in, SymbolResolver sr, boolean kludge)
    {
       this(new ByteBuffer(kludge ? new DotKludgeStream(in) : in));
       sym = sr;
       initializeKeywordDictionary();
       readCfg();
    }
+
+   /**
+    * Gets the filename of the artifact. If there is no artifact available, it will
+    * default to the original method.
+    *
+    * @return  The filename of the artifact.
+    */
+   @Override
+   public String getFilename()
+   {
+      if (artifact != null)
+      {
+         return artifact.getRelativePath();
+      }
+
+      return super.getFilename();
+   }
+
+   /**
+    * Used to retrieve the artifact.
+    *
+    * @return  The artifact.
+    */
+   public SourceArtifact getArtifact()
+   {
+      return artifact;
+   }
    
    /**
     * Initialize configuration values.

=== modified file 'src/com/goldencode/p2j/xml/XmlPatternWorker.java'
--- old/src/com/goldencode/p2j/xml/XmlPatternWorker.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/p2j/xml/XmlPatternWorker.java	2025-05-09 07:59:18 +0000
@@ -2,7 +2,7 @@
 ** Module   : XmlPatternWorker.java
 ** Abstract : Pattern worker which assists with XML AST processing
 **
-** Copyright (c) 2005-2023, Golden Code Development Corporation.
+** Copyright (c) 2005-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- --------------------------- Description ---------------------------
 ** 001 ECF 20050608   @21459 Created initial version. Provides a callback
@@ -31,7 +31,9 @@
 ** 014 CA  20150406          Fixed makeSystemID, when the URL protocol is "file". 
 ** 015 CA  20200412          Added incremental conversion support.
 ** 016 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 017 DDF 20250414          Make use of artifacts instead of the filename.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -93,6 +95,8 @@
 import java.util.*;
 import javax.xml.parsers.*;
 
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactManager;
 import com.goldencode.p2j.util.logging.*;
 import org.w3c.dom.*;
 import com.goldencode.ast.*;
@@ -332,7 +336,8 @@
          {
             try
             {
-               AstManager.get().saveTree(root, filename + ".xast", false);
+               Artifact artifact = ArtifactManager.addArtifact(filename + ".xast");
+               AstManager.get().saveTree(root, artifact, false);
             }
             catch (AstException exc)
             {

=== modified file 'src/com/goldencode/trpl/Driver.java'
--- old/src/com/goldencode/trpl/Driver.java	2023-05-12 10:05:12 +0000
+++ new/src/com/goldencode/trpl/Driver.java	2025-05-09 07:59:18 +0000
@@ -2,13 +2,15 @@
 ** Module   : Driver.java
 ** Abstract : thread which processes trees obtained from a queue of tree names 
 **
-** Copyright (c) 2009-2023, Golden Code Development Corporation.
+** Copyright (c) 2009-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------Description-----------------
 ** 001 GES 20090525   @42533 Thread which processes trees obtained from a queue
 **                           of tree names.
 ** 002 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
+** 003 DDF 20250414          Make use of artifacts instead of the filename.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -68,6 +70,7 @@
 import java.util.concurrent.*;
 import java.util.logging.*;
 
+import com.goldencode.artifacts.Artifact;
 import com.goldencode.ast.*;
 import com.goldencode.p2j.convert.*;
 
@@ -80,8 +83,8 @@
    /** Logger */
    private static final ConversionStatus LOG = ConversionStatus.get(Driver.class);
    
-   /** Queue from which to obtain the next tree name to process. */
-   private final ConcurrentLinkedQueue<String> queue;
+   /** Queue from which to obtain the next tree artifact to process. */
+   private final ConcurrentLinkedQueue<Artifact> queue;
    
    /** Callbacks to execute based on the events of the tree walk. */ 
    private final Pipeline pipeline;
@@ -99,7 +102,7 @@
     * Construct a driver thread instance.
     *
     * @param    queue
-    *           Queue from which to obtain the next tree name to process.
+    *           Queue from which to obtain the next tree artifact to process.
     * @param    pipeline
     *           Callbacks to execute based on the events of the tree walk.
     * @param    base
@@ -107,10 +110,10 @@
     * @param    latch
     *           Mechanism to notify the main engine thread of completion.
     */
-   public Driver(ConcurrentLinkedQueue<String> queue,
-                 Pipeline                      pipeline,
-                 BaseState                     base,
-                 CountDownLatch                latch)
+   public Driver(ConcurrentLinkedQueue<Artifact> queue,
+                 Pipeline                        pipeline,
+                 BaseState                       base,
+                 CountDownLatch                  latch)
    {
       this.queue    = queue;
       this.pipeline = pipeline;
@@ -131,11 +134,11 @@
       
       try
       {
-         String tname = queue.poll();
+         Artifact tartifact = queue.poll();
          
-         while (tname != null)
+         while (tartifact != null)
          {
-            long id = mgr.getTreeId(tname);
+            long id = mgr.getTreeId(tartifact);
             
             // check the tree's validity
             if (id != AstManager.INVALID_ID)
@@ -146,12 +149,12 @@
                   mgr.lockTree(id);
                   
                   // load the tree
-                  Aast      here     = mgr.loadTree(tname);
+                  Aast      here     = mgr.loadTree(tartifact);
                   WalkState state    = null;
                   Resolver  resolver = null;
                   
                   // TODO: remove this
-                  System.out.println(here.getFilename());
+                  System.out.println(here.getArtifact());
                
                   // iterate through each walker in the pipeline
                   for (WalkElement elem : code)
@@ -224,7 +227,7 @@
                   // persist changes if needed
                   if (pipeline.isAutoSave() && !pipeline.isReadOnly())
                   {
-                     mgr.saveTree(state.copy, tname, false);
+                     mgr.saveTree(state.copy, tartifact, false);
                      
                      // TODO: get the list of loaded ASTs and save each one
                   }
@@ -239,11 +242,11 @@
             else
             {
                // TODO: long this
-               System.out.printf("Invalid tree name %s!\n", tname);
+               System.out.printf("Invalid tree name %s!\n", tartifact);
             }
             
             // read the next tree name
-            tname = queue.poll();
+            tartifact = queue.poll();
          }
       }
       

=== modified file 'src/com/goldencode/trpl/Engine.java'
--- old/src/com/goldencode/trpl/Engine.java	2017-04-01 23:33:34 +0000
+++ new/src/com/goldencode/trpl/Engine.java	2025-05-09 07:59:18 +0000
@@ -2,12 +2,15 @@
 ** Module   : Engine.java
 ** Abstract : drives the multi-threaded processing of a list of trees 
 **
-** Copyright (c) 2009-2017, Golden Code Development Corporation.
+** Copyright (c) 2009-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- ----------------Description-----------------
 ** 001 GES 20090525   @42534 Drives the multi-threaded processing of a list of
 **                           trees.
+** 002 DDF 20250414          Make use of artifacts instead of the filename.
+**     DDF 20250424          Replaced the list of artifacts with ArtifactCollection.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -63,8 +66,10 @@
 
 package com.goldencode.trpl; 
  
-import java.util.*;
 import java.util.concurrent.*;
+
+import com.goldencode.artifacts.Artifact;
+import com.goldencode.artifacts.ArtifactCollection;
 import com.goldencode.ast.*;
 
 /**
@@ -90,18 +95,18 @@
    }
    
    /**
-    * Iterate through the list of tree names to be processed and for each one,
+    * Iterate through the list of tree artifacts to be processed and for each one,
     * traverse the tree and invoke the hooks of each tree walker in the
     * pipeline.
     *
     * @param    asts
-    *           List of tree names. Each one will be loaded in turn and
+    *           List of tree artifacts. Each one will be loaded in turn and
     *           optionally saved at the end (depending on the pipeline
     *           configuration).
     * @param    pipeline
     *           The tree walkers to be invoked and other configuration values.
     */
-   public void process(List<String> asts, Pipeline pipeline)
+   public void process(ArtifactCollection asts, Pipeline pipeline)
    {
       // common state setup
       BaseState base = new BaseState(mgr, this);
@@ -109,10 +114,9 @@
       // global init processing
       pipeline.init(base);
       
-      // create the queue which allows multiple threads to obtain tree names
+      // create the queue which allows multiple threads to obtain tree artifacts
       // to process
-      ConcurrentLinkedQueue<String> queue =
-         new ConcurrentLinkedQueue<String>(asts);
+      ConcurrentLinkedQueue<Artifact> queue = new ConcurrentLinkedQueue<Artifact>(asts.getArtifacts());
       
       int num = pipeline.getThreads();
       

