Project

General

Profile

experiment.patch

Teodor Gorghe, 09/25/2026 07:25 AM

Download (140 KB)

View differences:

new/build.gradle 2026-09-25 06:45:17 +0000
232 232
** 121 TG  20260805 Aligned commons-io, commons-lang3, commons-text and commons-configuration2
233 233
**                  across the client, server and convert configurations.
234 234
** 122 TG  20260825 Upgraded to the latest FWD-H2-1.65.
235
** 123 TG  20260924 ASM is a client and server dependency, used for the generated widget config accessors.
235 236
*/
236 237

  
237 238
/*
......
778 779
}
779 780

  
780 781
// ============================================================================
782
// Widget config accessors
783
// ============================================================================
784
tasks.register('generateConfigAccessors', JavaExec) {
785
    dependsOn 'aspectjWeave'
786
    def outDir = "${layout.buildDirectory.get()}/config-accessors"
787
    outputs.dir(outDir)
788
    classpath = files("${layout.buildDirectory.get()}/classes.aop") +
789
                configurations.fwdAllRuntime +
790
                configurations.aspectjCompile
791
    mainClass.set('com.goldencode.p2j.ui.ConfigAccessorGenerator')
792
    args outDir
793
    doFirst {
794
        delete outDir
795
    }
796
}
797

  
798
// ============================================================================
781 799
// Lifecycle task: gates all consumers of build/classes.aop/.
782 800
// Any task that writes to classes.aop should be added as a dependency here.
783 801
// ============================================================================
......
824 842
    from("${layout.buildDirectory.get()}/admin") {
825 843
        include 'com/goldencode/p2j/admin/**'
826 844
    }
845
    from(tasks.named('generateConfigAccessors'))
827 846
    // Include the empty SPI directory to match reference
828 847
    from("${layout.buildDirectory.get()}/classes.aop") {
829 848
        include 'com/goldencode/p2j/spi/'
......
1638 1657
    fwdClientServer group: 'org.reflections', name: 'reflections', version: '0.10.2'
1639 1658
    fwdClientServer group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.14'
1640 1659
    fwdClientServer group: 'org.javassist', name: 'javassist', version: '3.30.2-GA'
1660
    fwdClientServer group: 'org.ow2.asm', name: 'asm', version: '9.8'
1641 1661
    fwdClientServer group: 'org.apache.bcel', name: 'bcel', version: '6.10.0'
1642 1662
    fwdClientServer group: 'com.webauthn4j', name: 'webauthn4j-core', version: '0.31.7.RELEASE'
1643 1663
    fwdClientServer group: 'com.google.guava', name: 'guava-gwt', version: '33.4.6-jre'
......
1656 1676
    }
1657 1677
    
1658 1678
    fwdServer group: 'org.postgresql', name: 'postgresql', version: '42.7.8'
1659
    fwdServer group: 'org.ow2.asm', name: 'asm', version: '9.8'
1660 1679
    fwdServer group: 'com.mchange', name: 'c3p0', version: '0.14.1'
1661 1680
    fwdServer group: 'cglib', name: 'cglib', version: '3.3.0'
1662 1681
    fwdServer group: 'net.sf.ehcache', name: 'ehcache-core', version: '2.6.11'
new/src/com/goldencode/p2j/aspects/ui/ConfigFieldSetterAspect.java 2026-09-25 09:30:43 +0000
19 19
**                  of concrete class. This is needed to allow client-side code to be executed in
20 20
**                  the server context.
21 21
** 010 CA  20260804 Changes to allow the FWD Client to run in the same JVM as the FWD server.
22
** 011 TG  20260925 Configs with a virtual ID are skipped; the config caches its tracking config manager.
22 23
*/ 
23 24
/*
24 25
** This program is free software: you can redistribute it and/or modify
......
130 131
           "&& !within(com.goldencode.p2j.ui.*Entity) ")
131 132
   public void beforeSetField(WidgetConfig cfg, JoinPoint.StaticPart sp)
132 133
   {
134
      if (WidgetId.virtualWidget(cfg.id))
135
      {
136
         // not tracked, see ConfigManager.trackConfig
137
         return;
138
      }
139
      
133 140
      String field = sp.getSignature().getName();
134
      ClientConfigManager.getInstance().addDirtyConfig(cfg, field);
141
      cfg.trackingManager().addDirtyConfig(cfg, field);
135 142
   }
136 143
}
new/src/com/goldencode/p2j/aspects/ui/LocationCacheAspect.java 2026-09-25 08:51:10 +0000
9 9
** 002 CA  20180209 A fix - iajc compiler requires absolute names in the aspect definition. 
10 10
** 003 HC  20240806 Added support for displaying frame widget in an overlay window.
11 11
** 004 CA  20260804 Changes to allow the FWD Client to run in the same JVM as the FWD server.
12
** 005 TG  20260925 The layout depth is a mutable per-context counter, read once per join point.
12 13
*/
13 14
/*
14 15
** This program is free software: you can redistribute it and/or modify
......
88 89
    * Tracks the depth of layout-related nested API calls.  When is zero, it will force the
89 90
    * {@link AbstractWidget#reflectLocationChanged() cached values to be reset.}
90 91
    */
91
   private static final ContextLocal<Integer> local = new ContextLocal<>()
92
   private static final ContextLocal<int[]> local = new ContextLocal<>()
92 93
   {
93
      protected Integer initialValue() 
94
      protected int[] initialValue() 
94 95
      {
95
         return Integer.valueOf(0);
96
         return new int[1];
96 97
      }
97 98
   };
98 99
   
......
118 119
                                               Container<?> container)
119 120
   throws Throwable
120 121
   {
121
      int layoutDepth = local.get();
122
      int[] layoutDepth = local.get();
122 123
      AbstractWidget<?> widget = (AbstractWidget<?>) container;
123 124
      
124
      if (layoutDepth == 0)
125
      if (layoutDepth[0] == 0)
125 126
      {
126 127
         // ensure nothing is cached
127 128
         widget.reflectLocationChanged();
128 129
      }
129 130

  
130
      layoutDepth += 1;
131
      layoutDepth[0] += 1;
131 132
      
132 133
      try
133 134
      {
134
         local.set(layoutDepth);
135

  
136 135
         return thisJoinPoint.proceed();
137 136
      }
138 137
      finally
139 138
      {
140 139
         // no need to reflectLocationChange, as nothing gets cached while the depth is non-zero
141
         layoutDepth -= 1;
142

  
143
         local.set(layoutDepth);
140
         layoutDepth[0] -= 1;
144 141
      }
145 142
   }
146 143

  
......
163 160
   public Object disableLocationCacheInContainer(ProceedingJoinPoint thisJoinPoint)
164 161
   throws Throwable
165 162
   {
166
      int layoutDepth = local.get();
163
      int[] layoutDepth = local.get();
167 164
      
168 165
      AbstractWidget<?> widget = (AbstractWidget<?>) thisJoinPoint.getThis();
169 166

  
170
      if (layoutDepth == 0)
167
      if (layoutDepth[0] == 0)
171 168
      {
172 169
         // ensure nothing is cached
173 170
         widget.reflectLocationChanged();
174 171
      }
175 172

  
176
      layoutDepth += 1;
177

  
173
      layoutDepth[0] += 1;
174
      
178 175
      try
179 176
      {
180
         local.set(layoutDepth);
181
         
182 177
         return thisJoinPoint.proceed();
183 178
      }
184 179
      finally
185 180
      {
186 181
         // no need to reflectLocationChange, as nothing gets cached while the depth is non-zero
187
         layoutDepth -= 1;
188
         
189
         local.set(layoutDepth);
182
         layoutDepth[0] -= 1;
190 183
      }
191 184
   }
192 185
   
......
208 201
   public Object cacheScreenLocation(ProceedingJoinPoint thisJoinPoint)
209 202
   throws Throwable
210 203
   {
211
      int layoutDepth = local.get();
204
      int layoutDepth = local.get()[0];
212 205
      
213 206
      AbstractWidget<?> widget = (AbstractWidget<?>) thisJoinPoint.getThis();
214 207

  
......
249 242
   public Object cacheScreenPhysicalLocation(ProceedingJoinPoint thisJoinPoint)
250 243
   throws Throwable
251 244
   {
252
      int layoutDepth = local.get();
245
      int layoutDepth = local.get()[0];
253 246
      
254 247
      AbstractWidget<?> widget = (AbstractWidget<?>) thisJoinPoint.getThis();
255 248
      
new/src/com/goldencode/p2j/aspects/ui/SyncConfigChangesAspect.java 2026-09-25 09:30:39 +0000
13 13
**                  of concrete class. This is needed to allow client-side code to be executed in
14 14
**                  the server context.
15 15
** 005 RNC 20260402 Fixed javadoc warnings.
16
** 006 TG  20260925 The trackingManager() cache assignment is not a config value change.
16 17
*/
17 18
/*
18 19
** This program is free software: you can redistribute it and/or modify
......
151 152
    */
152 153
   @Before("target(config) && set(* *..WidgetConfig+.*)" +
153 154
           "&& !withincode(void readExternal(..)) " +
155
           "&& !withincode(* com.goldencode.p2j.ui.WidgetConfig.trackingManager()) " +
154 156
           "&& !withincode(void applyConfig(..)) " +
155 157
           "&& !withincode(private void com.goldencode.p2j.ui.LabelConfig.setDefaults(..)) " +
156 158
           "&& !withincode(com.goldencode.p2j.ui.*Config.new(..))" +
new/src/com/goldencode/p2j/main/StandardServer.java 2026-09-25 09:00:59 +0000
297 297
** 146 TG  20260703          Registered ChunkTransfer as its own network group so a peer (e.g. an
298 298
**                           appserver agent) can pull streamed binary payloads from this server.
299 299
** 147 CA  20260803          FWD Client can run entirely in the FWD Server JVM now, even in non-MSA mode.
300
** 148 TG  20260925          Server-side batch client thread uses the ContextLocal read cache.
300 301
*/
301 302

  
302 303
/*
......
1062 1063
            }
1063 1064

  
1064 1065
            session = SessionManager.get().initializeServerSession(context);
1066
            
1067
            // this thread stays in the new session's context until it terminates
1068
            ContextLocal.setReadCache(true);
1065 1069
         }
1066 1070
         catch (Exception e)
1067 1071
         {
......
1158 1162
         }
1159 1163
         finally
1160 1164
         {
1165
            ContextLocal.setReadCache(false);
1166
            
1161 1167
            try
1162 1168
            {
1163 1169
               // needs to be done first
......
1202 1208
         }
1203 1209
      };
1204 1210
      
1205
      Thread launcher = new Thread(runner);
1211
      Thread launcher = new ContextLocal.DedicatedThread(runner);
1206 1212
      launcher.setName("Launcher for server-side batch process " + account);
1207 1213
      launcher.setDaemon(true);
1208 1214
      launcher.start();
new/src/com/goldencode/p2j/net/SessionManager.java 2026-09-25 09:42:07 +0000
105 105
** 047 AS  20260617          Harden session context ID seeding with SecureRandom.
106 106
** 048 CA  20260803          When a FWD Client runs in the same JVM as the FWD Server, a mock net session is
107 107
**                           required to allow session-level listeners to execute.
108
** 049 TG  20260925          get() and isAsyncThread() do not use JVM-wide locks.
108 109
*/
109 110

  
110 111
/*
......
223 224
   /** Singleton instance of this class */
224 225
   private static SessionManager instance = null;
225 226
   
227
   /** The {@link #instance} after it was fully constructed, read without locking by {@link #get}. */
228
   private static volatile SessionManager published = null;
229
   
226 230
   /** Topology database for this network node */
227 231
   protected final Map<InetSocketAddress, Queue> topology = new HashMap<>();
228 232
   
......
275 279
   protected BootstrapConfig config = null;
276 280

  
277 281
   /** A set containing threads which always send async requests. */
278
   private final Set<Thread> asyncThreads = Collections.newSetFromMap(new IdentityHashMap<>());
282
   private final Set<Thread> asyncThreads = ConcurrentHashMap.newKeySet();
279 283
   
280 284
   /** The handler used to process interrupt requests. */
281 285
   private InterruptHandler interruptHandler;
......
335 339
    * @return  Singleton session manager, if it has been created;  otherwise,
336 340
    *          <code>null</code>.
337 341
    */
338
   public synchronized static SessionManager get()
342
   public static SessionManager get()
339 343
   {
340
      return instance;
344
      SessionManager sm = published;
345
      if (sm != null)
346
      {
347
         return sm;
348
      }
349
      
350
      // the instance is created while holding this lock; do not publish it if this thread is the one
351
      // constructing it
352
      boolean nested = Thread.holdsLock(SessionManager.class);
353
      synchronized (SessionManager.class)
354
      {
355
         sm = instance;
356
         if (!nested)
357
         {
358
            published = sm;
359
         }
360
      }
361
      
362
      return sm;
341 363
   }
342 364
   
343 365
   /**
......
348 370
    */
349 371
   public final void registerAsyncThread(Thread thread)
350 372
   {
351
      synchronized (asyncThreads)
352
      {
353
         asyncThreads.add(thread);
354
      }
373
      asyncThreads.add(thread);
355 374
   }
356 375
   
357 376
   /**
......
362 381
    */
363 382
   public final void deregisterAsyncThread(Thread thread)
364 383
   {
365
      synchronized (asyncThreads)
366
      {
367
         asyncThreads.remove(thread);
368
      }
384
      asyncThreads.remove(thread);
369 385
   }
370 386
   
371 387
   /**
......
379 395
    */
380 396
   public final boolean isAsyncThread(Thread thread)
381 397
   {
382
      synchronized (asyncThreads)
383
      {
384
         return asyncThreads.contains(thread);
385
      }
398
      // checked twice for each call of an in-JVM client, without a JVM-wide lock
399
      return !asyncThreads.isEmpty() && asyncThreads.contains(thread);
386 400
   }
387 401
   
388 402
   /**
new/src/com/goldencode/p2j/security/ContextLocal.java 2026-09-25 09:08:04 +0000
88 88
** 029 CA  20240903          For standalone mode, 'fallback' thread-local var must be assigned on first usage
89 89
**                           in get/set.
90 90
** 030 CA  20260803          Standalone FWD Client JVMs must also clean up this data.
91
** 031 TG  20260925          Added DedicatedThread, which can use the read cache like conversation threads.
91 92
*/
92 93

  
93 94
/*
......
811 812
    */
812 813
   private static boolean hasReadCache()
813 814
   {
814
      return SessionManager.isConversationThread();
815
      if (SessionManager.isConversationThread())
816
      {
817
         return true;
818
      }
819
      
820
      Thread t = Thread.currentThread();
821
      return t instanceof DedicatedThread && ((DedicatedThread) t).readCache;
822
   }
823
   
824
   /**
825
    * Enable or disable the read cache for the current thread, which must be a {@link DedicatedThread}.
826
    * The cache must be enabled only while the thread stays in the same security context.
827
    * 
828
    * @param    on
829
    *           <code>true</code> to enable the read cache.
830
    */
831
   public static void setReadCache(boolean on)
832
   {
833
      Thread t = Thread.currentThread();
834
      if (!(t instanceof DedicatedThread))
835
      {
836
         throw new IllegalStateException("The read cache can be enabled only on a DedicatedThread.");
837
      }
838
      
839
      ((DedicatedThread) t).readCache = on;
840
   }
841
   
842
   /**
843
    * A thread which is dedicated to a single security context for (a part of) its lifetime, like a
844
    * conversation thread.  While it is in that context, it can enable the read cache used to optimize
845
    * {@link ContextLocal#get()}, via {@link ContextLocal#setReadCache}.
846
    */
847
   public static class DedicatedThread
848
   extends Thread
849
   {
850
      /** Flag indicating if the read cache is enabled; accessed only by this thread. */
851
      private boolean readCache = false;
852
      
853
      /**
854
       * Create a new thread.
855
       * 
856
       * @param    target
857
       *           The code to run.
858
       */
859
      public DedicatedThread(Runnable target)
860
      {
861
         super(target);
862
      }
815 863
   }
816 864
   
817 865
   /**
new/src/com/goldencode/p2j/ui/ClientConfigManager.java 2026-09-25 10:20:48 +0000
4 4
**
5 5
** Copyright (c) 2014-2026, Golden Code Development Corporation.
6 6
**
7
** -#- -I- --Date-- ---------------------------------Description----------------------------------
7
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
8 8
** 001 CA  20140929 Created initial version.
9 9
** 002 CA  20141014 Separated data which needs to survive the appserver agent's context reset into 
10 10
**                  a different container.
......
41 41
** 023 TG  20260414 Replaced ReflectASM with MethodHandles.
42 42
** 024 CA  20260803 FWD Client can run entirely in the FWD Server JVM now - so any constant field is marked as
43 43
**                  an explicit constant.
44
** 025 TG  20260924 Field access via generated ConfigAccessor; added setConfigFields for batched updates.
45
** 026 TG  20260925 Added setBackupField(s), to apply server-pushed values to a backup config.
46
** 027 TG  20260925 Added snapshotConfig and firstDifference, used by FrameLayoutCache.
44 47
*/
45 48

  
46 49
/*
......
198 201
   }
199 202

  
200 203
   /**
204
    * Set the widget config fields to the specified values.
205
    *
206
    * @param    wid
207
    *           The widget ID.
208
    * @param    wcfg
209
    *           The widget configuration; if this is a down widget, this will be used.
210
    * @param    fids
211
    *           The config field ids.
212
    * @param    values
213
    *           The new config field values.
214
    */
215
   public void setConfigFields(WidgetId wid, WidgetConfig wcfg, Integer[] fids, Object[] values)
216
   {
217
      if (!(wid instanceof WidgetDownId))
218
      {
219
         wcfg = getActiveConfig(wid);
220
      }
221
      WidgetConfigDef wdef = getConfigDef(wid.asInt());
222

  
223
      int[] ids = new int[fids.length];
224
      for (int i = 0; i < fids.length; i++)
225
      {
226
         ids[i] = fids[i];
227
      }
228

  
229
      setWidgetConfigs(wcfg, wdef, ids, values);
230
   }
231

  
232
   /**
233
    * Apply the values of the specified fields, pushed by the server-side, to the backup config of a
234
    * (non-down) widget, so the backup config reflects what the server-side knows.  Only fields tracked
235
    * for changed values (immutable types) are applied; the other fields are always sent when dirty.
236
    *
237
    * @param    wid
238
    *           The widget ID.
239
    * @param    fname
240
    *           The config field name.
241
    * @param    value
242
    *           The value pushed by the server-side.
243
    */
244
   public void setBackupField(WidgetId wid, String fname, Object value)
245
   {
246
      WidgetConfig backup = (wid instanceof WidgetDownId) ? null : backupConfigs.get(wid);
247
      if (backup == null)
248
      {
249
         return;
250
      }
251
      
252
      WidgetConfigDef wdef = getConfigDef(wid.asInt());
253
      Integer fid = wdef.fieldIds.get(fname);
254
      if (fid != null && wdef.trackChanged[fid])
255
      {
256
         // the backup config has no owner, it must not be registered for state synchronization
257
         super.setWidgetConfig(backup, value, wdef, fid);
258
      }
259
   }
260

  
261
   /**
262
    * Apply the values of the specified fields, pushed by the server-side, to the backup config of a
263
    * (non-down) widget; see {@link #setBackupField}.
264
    *
265
    * @param    wid
266
    *           The widget ID.
267
    * @param    fids
268
    *           The config field ids.
269
    * @param    values
270
    *           The values pushed by the server-side.
271
    */
272
   public void setBackupFields(WidgetId wid, Integer[] fids, Object[] values)
273
   {
274
      WidgetConfig backup = (wid instanceof WidgetDownId) ? null : backupConfigs.get(wid);
275
      if (backup == null)
276
      {
277
         return;
278
      }
279
      
280
      WidgetConfigDef wdef = getConfigDef(wid.asInt());
281
      for (int i = 0; i < fids.length; i++)
282
      {
283
         int fid = fids[i];
284
         if (wdef.trackChanged[fid])
285
         {
286
            super.setWidgetConfig(backup, values[i], wdef, fid);
287
         }
288
      }
289
   }
290

  
291
   /**
292
    * Create a copy of the given config, which is not registered anywhere and has no ID.  It is meant
293
    * for comparisons, see {@link #firstDifference}.
294
    *
295
    * @param    cfg
296
    *           The config to copy.
297
    *
298
    * @return   The copy.
299
    */
300
   public WidgetConfig snapshotConfig(WidgetConfig cfg)
301
   {
302
      WidgetConfig copy = getConfigDef(cfg.cfgClass).newInstance();
303
      ConfigSyncManager.applyUnregistered(copy, cfg);
304
      
305
      return copy;
306
   }
307
   
308
   /**
309
    * Find the first field which has different values in the given configs of the same class.
310
    *
311
    * @param    a
312
    *           The first config.
313
    * @param    b
314
    *           The second config.
315
    * @param    ignored
316
    *           The names of the fields which are not compared.
317
    *
318
    * @return   The name of the first different field, or <code>null</code> if all compared fields have
319
    *           the same values.
320
    */
321
   public String firstDifference(WidgetConfig a, WidgetConfig b, Set<String> ignored)
322
   {
323
      if (a.cfgClass != b.cfgClass)
324
      {
325
         return "<class>";
326
      }
327
      
328
      WidgetConfigDef wdef = getConfigDef(a.cfgClass);
329
      for (int fid = 0; fid < wdef.fieldCount; fid++)
330
      {
331
         if (!wdef.accessor.same(a, b, fid))
332
         {
333
            String name = wdef.fieldNames.get(fid);
334
            if (!ignored.contains(name))
335
            {
336
               return name;
337
            }
338
         }
339
      }
340
      
341
      return null;
342
   }
343

  
344
   /**
201 345
    * Inform the client-side that it can no longer send config updates.
202 346
    */
203 347
   public void deactivateConfigUpdates()
......
289 433

  
290 434
         try
291 435
         {
292
            Object activeVal = wdef.fields[fid].get(cfg);
293
            Object backupVal = wdef.fields[fid].get(backup);
294

  
295
            boolean changed = !((activeVal == null && backupVal == null) ||
296
                                (activeVal != null && activeVal.equals(backupVal)));
297
            
298
            if (changed)
436
            if (!wdef.accessor.same(cfg, backup, fid))
299 437
            {
300 438
               addDirtyConfig(cfg, fname);
301 439
            }
......
367 505
      ConfigSyncManager.registerConfig(null, cfg);
368 506
      super.setWidgetConfig(cfg, value, wdef, fid);
369 507
   }
508

  
509
   /**
510
    * Set the specified widget configuration fields to the given values.
511
    *
512
    * @param    cfg
513
    *           The widget configuration to be updated.
514
    * @param    wdef
515
    *           The definition of the widget configuration.
516
    * @param    fids
517
    *           The IDs of the changed fields.
518
    * @param    values
519
    *           The new field values.
520
    */
521
   @Override
522
   <T extends WidgetConfig> void setWidgetConfigs(T               cfg,
523
                                                  WidgetConfigDef wdef,
524
                                                  int[]           fids,
525
                                                  Object[]        values)
526
   {
527
      ConfigSyncManager.registerConfig(null, cfg);
528
      super.setWidgetConfigs(cfg, wdef, fids, values);
529
   }
370 530
   
371 531
   /**
372 532
    * Helper method to replace the entry in the given map, to use the new key. 
new/src/com/goldencode/p2j/ui/ConfigAccessor.java 2026-09-25 06:45:17 +0000
1
/*
2
** Module   : ConfigAccessor.java
3
** Abstract : Indexed access to the tracked fields of a widget configuration class.
4
**
5
** Copyright (c) 2026, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
8
** 001 TG  20260924 Created initial version.
9
*/
10

  
11
/*
12
** This program is free software: you can redistribute it and/or modify
13
** it under the terms of the GNU Affero General Public License as
14
** published by the Free Software Foundation, either version 3 of the
15
** License, or (at your option) any later version.
16
**
17
** This program is distributed in the hope that it will be useful,
18
** but WITHOUT ANY WARRANTY; without even the implied warranty of
19
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
** GNU Affero General Public License for more details.
21
**
22
** You may find a copy of the GNU Affero GPL version 3 at the following
23
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
24
** 
25
** Additional terms under GNU Affero GPL version 3 section 7:
26
** 
27
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
28
**   terms apply to the works covered under the License.  These additional terms
29
**   are non-permissive additional terms allowed under Section 7 of the GNU
30
**   Affero GPL version 3 and may not be removed by you.
31
** 
32
**   0. Attribution Requirement.
33
** 
34
**     You must preserve all legal notices or author attributions in the covered
35
**     work or Appropriate Legal Notices displayed by works containing the covered
36
**     work.  You may not remove from the covered work any author or developer
37
**     credit already included within the covered work.
38
** 
39
**   1. No License To Use Trademarks.
40
** 
41
**     This license does not grant any license or rights to use the trademarks
42
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
43
**     of Golden Code Development Corporation. You are not authorized to use the
44
**     name Golden Code, FWD, or the names of any author or contributor, for
45
**     publicity purposes without written authorization.
46
** 
47
**   2. No Misrepresentation of Affiliation.
48
** 
49
**     You may not represent yourself as Golden Code Development Corporation or FWD.
50
** 
51
**     You may not represent yourself for publicity purposes as associated with
52
**     Golden Code Development Corporation, FWD, or any author or contributor to
53
**     the covered work, without written authorization.
54
** 
55
**   3. No Misrepresentation of Source or Origin.
56
** 
57
**     You may not represent the covered work as solely your work.  All modified
58
**     versions of the covered work must be marked in a reasonable way to make it
59
**     clear that the modified work is not originating from Golden Code Development
60
**     Corporation or FWD.  All modified versions must contain the notices of
61
**     attribution required in this license.
62
*/
63

  
64

  
65
package com.goldencode.p2j.ui;
66

  
67
import java.util.*;
68

  
69
/**
70
 * Provides access to the tracked fields of a single {@link WidgetConfig} class, using the field IDs
71
 * defined by the associated {@link WidgetConfigDef}.
72
 * <p>
73
 * Implementations are generated by {@link ConfigAccessorGenerator}, one class for each widget
74
 * configuration class, so that each field access compiles to a direct field instruction.  The
75
 * accessors of the built-in configuration classes are generated at build time; see
76
 * {@link ConfigAccessorFactory}.
77
 * The bulk operations run their loop inside the generated class, so that the per-field calls are
78
 * bound to a single receiver type.
79
 * <p>
80
 * The public static helpers are invoked by the generated code and reproduce the conversions
81
 * applied by a {@link java.lang.invoke.VarHandle} invoked with {@code Object} values, and the
82
 * equality applied by {@link Objects#equals(Object, Object)} on boxed values.
83
 */
84
public abstract class ConfigAccessor
85
{
86
   /**
87
    * Constructor for the sub-classes.
88
    */
89
   protected ConfigAccessor()
90
   {
91
   }
92

  
93
   /**
94
    * Unbox a value assigned to a {@code boolean} field.
95
    *
96
    * @param    v
97
    *           The value.
98
    *
99
    * @return   See above.
100
    *
101
    * @throws   NullPointerException
102
    *           If the value is {@code null}.
103
    * @throws   ClassCastException
104
    *           If the value can not be converted.
105
    */
106
   public static boolean unboxBoolean(Object v)
107
   {
108
      if (v instanceof Boolean)
109
      {
110
         return ((Boolean) v).booleanValue();
111
      }
112

  
113
      throw wrongType(v, boolean.class);
114
   }
115

  
116
   /**
117
    * Unbox a value assigned to a {@code byte} field.
118
    *
119
    * @param    v
120
    *           The value.
121
    *
122
    * @return   See above.
123
    *
124
    * @throws   NullPointerException
125
    *           If the value is {@code null}.
126
    * @throws   ClassCastException
127
    *           If the value can not be converted.
128
    */
129
   public static byte unboxByte(Object v)
130
   {
131
      if (v instanceof Byte)
132
      {
133
         return ((Byte) v).byteValue();
134
      }
135

  
136
      throw wrongType(v, byte.class);
137
   }
138

  
139
   /**
140
    * Unbox a value assigned to a {@code short} field, allowing primitive widening.
141
    *
142
    * @param    v
143
    *           The value.
144
    *
145
    * @return   See above.
146
    *
147
    * @throws   NullPointerException
148
    *           If the value is {@code null}.
149
    * @throws   ClassCastException
150
    *           If the value can not be converted.
151
    */
152
   public static short unboxShort(Object v)
153
   {
154
      if (v instanceof Short)
155
      {
156
         return ((Short) v).shortValue();
157
      }
158

  
159
      if (v instanceof Byte)
160
      {
161
         return ((Byte) v).byteValue();
162
      }
163

  
164
      throw wrongType(v, short.class);
165
   }
166

  
167
   /**
168
    * Unbox a value assigned to a {@code char} field.
169
    *
170
    * @param    v
171
    *           The value.
172
    *
173
    * @return   See above.
174
    *
175
    * @throws   NullPointerException
176
    *           If the value is {@code null}.
177
    * @throws   ClassCastException
178
    *           If the value can not be converted.
179
    */
180
   public static char unboxChar(Object v)
181
   {
182
      if (v instanceof Character)
183
      {
184
         return ((Character) v).charValue();
185
      }
186

  
187
      throw wrongType(v, char.class);
188
   }
189

  
190
   /**
191
    * Unbox a value assigned to an {@code int} field, allowing primitive widening.
192
    *
193
    * @param    v
194
    *           The value.
195
    *
196
    * @return   See above.
197
    *
198
    * @throws   NullPointerException
199
    *           If the value is {@code null}.
200
    * @throws   ClassCastException
201
    *           If the value can not be converted.
202
    */
203
   public static int unboxInt(Object v)
204
   {
205
      if (v instanceof Integer)
206
      {
207
         return ((Integer) v).intValue();
208
      }
209

  
210
      if (v instanceof Short || v instanceof Byte)
211
      {
212
         return ((Number) v).intValue();
213
      }
214

  
215
      if (v instanceof Character)
216
      {
217
         return ((Character) v).charValue();
218
      }
219

  
220
      throw wrongType(v, int.class);
221
   }
222

  
223
   /**
224
    * Unbox a value assigned to a {@code long} field, allowing primitive widening.
225
    *
226
    * @param    v
227
    *           The value.
228
    *
229
    * @return   See above.
230
    *
231
    * @throws   NullPointerException
232
    *           If the value is {@code null}.
233
    * @throws   ClassCastException
234
    *           If the value can not be converted.
235
    */
236
   public static long unboxLong(Object v)
237
   {
238
      if (v instanceof Long)
239
      {
240
         return ((Long) v).longValue();
241
      }
242

  
243
      if (v instanceof Integer || v instanceof Short || v instanceof Byte)
244
      {
245
         return ((Number) v).longValue();
246
      }
247

  
248
      if (v instanceof Character)
249
      {
250
         return ((Character) v).charValue();
251
      }
252

  
253
      throw wrongType(v, long.class);
254
   }
255

  
256
   /**
257
    * Unbox a value assigned to a {@code float} field, allowing primitive widening.
258
    *
259
    * @param    v
260
    *           The value.
261
    *
262
    * @return   See above.
263
    *
264
    * @throws   NullPointerException
265
    *           If the value is {@code null}.
266
    * @throws   ClassCastException
267
    *           If the value can not be converted.
268
    */
269
   public static float unboxFloat(Object v)
270
   {
271
      if (v instanceof Float)
272
      {
273
         return ((Float) v).floatValue();
274
      }
275

  
276
      if (v instanceof Long || v instanceof Integer || v instanceof Short || v instanceof Byte)
277
      {
278
         return ((Number) v).floatValue();
279
      }
280

  
281
      if (v instanceof Character)
282
      {
283
         return ((Character) v).charValue();
284
      }
285

  
286
      throw wrongType(v, float.class);
287
   }
288

  
289
   /**
290
    * Unbox a value assigned to a {@code double} field, allowing primitive widening.
291
    *
292
    * @param    v
293
    *           The value.
294
    *
295
    * @return   See above.
296
    *
297
    * @throws   NullPointerException
298
    *           If the value is {@code null}.
299
    * @throws   ClassCastException
300
    *           If the value can not be converted.
301
    */
302
   public static double unboxDouble(Object v)
303
   {
304
      if (v instanceof Double)
305
      {
306
         return ((Double) v).doubleValue();
307
      }
308

  
309
      if (v instanceof Float   ||
310
          v instanceof Long    ||
311
          v instanceof Integer ||
312
          v instanceof Short   ||
313
          v instanceof Byte)
314
      {
315
         return ((Number) v).doubleValue();
316
      }
317

  
318
      if (v instanceof Character)
319
      {
320
         return ((Character) v).charValue();
321
      }
322

  
323
      throw wrongType(v, double.class);
324
   }
325

  
326
   /**
327
    * Compare two {@code int} compatible field values.
328
    *
329
    * @param    a
330
    *           The first value.
331
    * @param    b
332
    *           The second value.
333
    *
334
    * @return   {@code true} if the values are equal.
335
    */
336
   public static boolean same(int a, int b)
337
   {
338
      return a == b;
339
   }
340

  
341
   /**
342
    * Compare two {@code long} field values.
343
    *
344
    * @param    a
345
    *           The first value.
346
    * @param    b
347
    *           The second value.
348
    *
349
    * @return   {@code true} if the values are equal.
350
    */
351
   public static boolean same(long a, long b)
352
   {
353
      return a == b;
354
   }
355

  
356
   /**
357
    * Compare two {@code float} field values, as {@link Float#equals(Object)} does.
358
    *
359
    * @param    a
360
    *           The first value.
361
    * @param    b
362
    *           The second value.
363
    *
364
    * @return   {@code true} if the values are equal.
365
    */
366
   public static boolean same(float a, float b)
367
   {
368
      return Float.floatToIntBits(a) == Float.floatToIntBits(b);
369
   }
370

  
371
   /**
372
    * Compare two {@code double} field values, as {@link Double#equals(Object)} does.
373
    *
374
    * @param    a
375
    *           The first value.
376
    * @param    b
377
    *           The second value.
378
    *
379
    * @return   {@code true} if the values are equal.
380
    */
381
   public static boolean same(double a, double b)
382
   {
383
      return Double.doubleToLongBits(a) == Double.doubleToLongBits(b);
384
   }
385

  
386
   /**
387
    * Compare two reference field values.
388
    *
389
    * @param    a
390
    *           The first value.
391
    * @param    b
392
    *           The second value.
393
    *
394
    * @return   {@code true} if the values are equal.
395
    */
396
   public static boolean same(Object a, Object b)
397
   {
398
      return Objects.equals(a, b);
399
   }
400

  
401
   /**
402
    * Create the exception thrown for a field ID which is not defined by the config class.
403
    *
404
    * @param    fid
405
    *           The field ID.
406
    *
407
    * @return   See above.
408
    */
409
   public static RuntimeException unknownField(int fid)
410
   {
411
      return new IllegalArgumentException("Unknown widget config field ID " + fid);
412
   }
413

  
414
   /**
415
    * Get the fingerprint of the fields this accessor was generated for, as computed by
416
    * {@link ConfigAccessorFactory#fingerprint}.
417
    *
418
    * @return   See above.
419
    */
420
   public abstract long fingerprint();
421

  
422
   /**
423
    * Get the value of a field.
424
    *
425
    * @param    cfg
426
    *           The widget config.
427
    * @param    fid
428
    *           The field ID.
429
    *
430
    * @return   The field value, boxed if the field is primitive.
431
    */
432
   public abstract Object get(WidgetConfig cfg, int fid);
433

  
434
   /**
435
    * Set the value of a field.
436
    *
437
    * @param    cfg
438
    *           The widget config.
439
    * @param    fid
440
    *           The field ID.
441
    * @param    value
442
    *           The new value, boxed if the field is primitive.
443
    */
444
   public abstract void set(WidgetConfig cfg, int fid, Object value);
445

  
446
   /**
447
    * Check if a field has the same value in two configs.
448
    *
449
    * @param    a
450
    *           The first widget config.
451
    * @param    b
452
    *           The second widget config.
453
    * @param    fid
454
    *           The field ID.
455
    *
456
    * @return   {@code true} if the values are equal.
457
    */
458
   public abstract boolean same(WidgetConfig a, WidgetConfig b, int fid);
459

  
460
   /**
461
    * Collect the values of the given fields from the active config.  A field which is tracked
462
    * for changed value is skipped when its value is the same in the backup config.
463
    *
464
    * @param    active
465
    *           The active widget config.
466
    * @param    backup
467
    *           The backup widget config.
468
    * @param    fids
469
    *           The IDs of the candidate fields.
470
    * @param    count
471
    *           The number of candidate fields in {@code fids}.
472
    * @param    track
473
    *           Flags indicating the fields tracked for changed value, indexed by field ID, or
474
    *           {@code null} to collect all candidate fields.
475
    * @param    outFids
476
    *           Receives the IDs of the collected fields.
477
    * @param    outVals
478
    *           Receives the values of the collected fields.
479
    *
480
    * @return   The number of collected fields.
481
    */
482
   public abstract int collectChanges(WidgetConfig active,
483
                                      WidgetConfig backup,
484
                                      int[]        fids,
485
                                      int          count,
486
                                      boolean[]    track,
487
                                      int[]        outFids,
488
                                      Object[]     outVals);
489

  
490
   /**
491
    * Set the values of the given fields.
492
    *
493
    * @param    cfg
494
    *           The widget config.
495
    * @param    fids
496
    *           The field IDs.
497
    * @param    values
498
    *           The new values, boxed for primitive fields.
499
    * @param    count
500
    *           The number of fields to set.
501
    */
502
   public abstract void applyUpdates(WidgetConfig cfg, int[] fids, Object[] values, int count);
503

  
504
   /**
505
    * Create the exception thrown when a value can not be assigned to a primitive field.
506
    *
507
    * @param    v
508
    *           The value.
509
    * @param    type
510
    *           The primitive field type.
511
    *
512
    * @return   See above.
513
    *
514
    * @throws   NullPointerException
515
    *           If the value is {@code null}.
516
    */
517
   private static RuntimeException wrongType(Object v, Class<?> type)
518
   {
519
      if (v == null)
520
      {
521
         throw new NullPointerException("Can not assign null to a " + type.getName() + " field");
522
      }
523

  
524
      return new ClassCastException("Cannot convert " + v.getClass().getName() + " to " + type.getName());
525
   }
526
}
new/src/com/goldencode/p2j/ui/ConfigAccessorFactory.java 2026-09-25 06:45:17 +0000
1
/*
2
** Module   : ConfigAccessorFactory.java
3
** Abstract : Resolution of the prebuilt or runtime generated widget configuration field accessors.
4
**
5
** Copyright (c) 2026, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
8
** 001 TG  20260924 Created initial version.
9
*/
10

  
11
/*
12
** This program is free software: you can redistribute it and/or modify
13
** it under the terms of the GNU Affero General Public License as
14
** published by the Free Software Foundation, either version 3 of the
15
** License, or (at your option) any later version.
16
**
17
** This program is distributed in the hope that it will be useful,
18
** but WITHOUT ANY WARRANTY; without even the implied warranty of
19
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
** GNU Affero General Public License for more details.
21
**
22
** You may find a copy of the GNU Affero GPL version 3 at the following
23
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
24
** 
25
** Additional terms under GNU Affero GPL version 3 section 7:
26
** 
27
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
28
**   terms apply to the works covered under the License.  These additional terms
29
**   are non-permissive additional terms allowed under Section 7 of the GNU
30
**   Affero GPL version 3 and may not be removed by you.
31
** 
32
**   0. Attribution Requirement.
33
** 
34
**     You must preserve all legal notices or author attributions in the covered
35
**     work or Appropriate Legal Notices displayed by works containing the covered
36
**     work.  You may not remove from the covered work any author or developer
37
**     credit already included within the covered work.
38
** 
39
**   1. No License To Use Trademarks.
40
** 
41
**     This license does not grant any license or rights to use the trademarks
42
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
43
**     of Golden Code Development Corporation. You are not authorized to use the
44
**     name Golden Code, FWD, or the names of any author or contributor, for
45
**     publicity purposes without written authorization.
46
** 
47
**   2. No Misrepresentation of Affiliation.
48
** 
49
**     You may not represent yourself as Golden Code Development Corporation or FWD.
50
** 
51
**     You may not represent yourself for publicity purposes as associated with
52
**     Golden Code Development Corporation, FWD, or any author or contributor to
53
**     the covered work, without written authorization.
54
** 
55
**   3. No Misrepresentation of Source or Origin.
56
** 
57
**     You may not represent the covered work as solely your work.  All modified
58
**     versions of the covered work must be marked in a reasonable way to make it
59
**     clear that the modified work is not originating from Golden Code Development
60
**     Corporation or FWD.  All modified versions must contain the notices of
61
**     attribution required in this license.
62
*/
63

  
64

  
65
package com.goldencode.p2j.ui;
66

  
67
import java.lang.reflect.*;
68
import java.nio.charset.*;
69

  
70
import com.goldencode.p2j.util.logging.*;
71

  
72
/**
73
 * Resolves the {@link ConfigAccessor} for a widget configuration class.
74
 * <p>
75
 * The accessors of the built-in configuration classes are generated at build time by
76
 * {@link ConfigAccessorGenerator} and packaged in the FWD jar, named with the
77
 * {@link #PREBUILT_PREFIX} prefix followed by the configuration class name.  Being regular classes
78
 * loaded from the jar, they can be included in an AppCDS archive.  A prebuilt accessor is used only
79
 * if its {@link ConfigAccessor#fingerprint() fingerprint} matches the fields of the configuration
80
 * class; otherwise, and for the configuration classes without a prebuilt accessor, the accessor is
81
 * generated at runtime as a hidden class.
82
 * <p>
83
 * This class does not reference the bytecode generation library, so it is loaded only when an
84
 * accessor needs to be generated at runtime.
85
 */
86
final class ConfigAccessorFactory
87
{
88
   /** The class name prefix of the prebuilt accessors. */
89
   static final String PREBUILT_PREFIX = "com.goldencode.p2j.ui.ConfigAccessors$";
90

  
91
   /** Logger */
92
   private static final CentralLogger LOG = CentralLogger.get(ConfigAccessorFactory.class.getSimpleName());
93

  
94
   /** The package of the built-in widget configuration classes. */
95
   private static final String CONFIG_PACKAGE = WidgetConfig.class.getPackageName();
96

  
97
   /**
98
    * Private constructor, this class has only static members.
99
    */
100
   private ConfigAccessorFactory()
101
   {
102
   }
103

  
104
   /**
105
    * Get the accessor for the given widget configuration class.
106
    *
107
    * @param    cfgCls
108
    *           The widget configuration class.
109
    * @param    fields
110
    *           The tracked fields, indexed by field ID.
111
    *
112
    * @return   See above.
113
    */
114
   static ConfigAccessor create(Class<? extends WidgetConfig> cfgCls, Field[] fields)
115
   {
116
      long           fp  = fingerprint(fields);
117
      ConfigAccessor acc = loadPrebuilt(cfgCls);
118

  
119
      if (acc != null)
120
      {
121
         if (acc.fingerprint() == fp)
122
         {
123
            return acc;
124
         }
125

  
126
         LOG.warning("The prebuilt config accessor for " + cfgCls.getName() +
127
                     " does not match the config fields, generating it at runtime.");
128
      }
129

  
130
      return ConfigAccessorGenerator.define(cfgCls, fields, fp);
131
   }
132

  
133
   /**
134
    * Get the name of the prebuilt accessor of the given widget configuration class.
135
    *
136
    * @param    cfgCls
137
    *           The widget configuration class.
138
    *
139
    * @return   The class name, or {@code null} if the class is not a built-in configuration class.
140
    */
141
   static String prebuiltName(Class<?> cfgCls)
142
   {
143
      if (!CONFIG_PACKAGE.equals(cfgCls.getPackageName()))
144
      {
145
         return null;
146
      }
147

  
148
      return PREBUILT_PREFIX + cfgCls.getName().substring(CONFIG_PACKAGE.length() + 1);
149
   }
150

  
151
   /**
152
    * Compute the fingerprint of the tracked fields: a 64-bit FNV-1a hash of the field names and
153
    * type descriptors, in field ID order.
154
    *
155
    * @param    fields
156
    *           The tracked fields, indexed by field ID.
157
    *
158
    * @return   See above.
159
    */
160
   static long fingerprint(Field[] fields)
161
   {
162
      long hash = 0xcbf29ce484222325L;
163
      for (Field f : fields)
164
      {
165
         String sig = f.getName() + ':' + f.getType().descriptorString() + ';';
166
         for (byte b : sig.getBytes(StandardCharsets.UTF_8))
167
         {
168
            hash ^= (b & 0xff);
169
            hash *= 0x100000001b3L;
170
         }
171
      }
172

  
173
      return hash;
174
   }
175

  
176
   /**
177
    * Load and instantiate the prebuilt accessor of the given widget configuration class.
178
    *
179
    * @param    cfgCls
180
    *           The widget configuration class.
181
    *
182
    * @return   The accessor, or {@code null} if there is no prebuilt accessor for the class.
183
    */
184
   private static ConfigAccessor loadPrebuilt(Class<? extends WidgetConfig> cfgCls)
185
   {
186
      String name = prebuiltName(cfgCls);
187
      if (name == null)
188
      {
189
         return null;
190
      }
191

  
192
      Class<?> cls;
193
      try
194
      {
195
         cls = Class.forName(name, true, ConfigAccessor.class.getClassLoader());
196
      }
197
      catch (ClassNotFoundException e)
198
      {
199
         return null;
200
      }
201

  
202
      try
203
      {
204
         return (ConfigAccessor) cls.getDeclaredConstructor().newInstance();
205
      }
206
      catch (ReflectiveOperationException e)
207
      {
208
         throw new IllegalStateException("Could not instantiate the config accessor " + name, e);
209
      }
210
   }
211
}
new/src/com/goldencode/p2j/ui/ConfigAccessorGenerator.java 2026-09-25 06:45:17 +0000
1
/*
2
** Module   : ConfigAccessorGenerator.java
3
** Abstract : Bytecode generation of the widget configuration field accessors.
4
**
5
** Copyright (c) 2026, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
8
** 001 TG  20260924 Created initial version.
9
*/
10

  
11
/*
12
** This program is free software: you can redistribute it and/or modify
13
** it under the terms of the GNU Affero General Public License as
14
** published by the Free Software Foundation, either version 3 of the
15
** License, or (at your option) any later version.
16
**
17
** This program is distributed in the hope that it will be useful,
18
** but WITHOUT ANY WARRANTY; without even the implied warranty of
19
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
** GNU Affero General Public License for more details.
21
**
22
** You may find a copy of the GNU Affero GPL version 3 at the following
23
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
24
** 
25
** Additional terms under GNU Affero GPL version 3 section 7:
26
** 
27
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
28
**   terms apply to the works covered under the License.  These additional terms
29
**   are non-permissive additional terms allowed under Section 7 of the GNU
30
**   Affero GPL version 3 and may not be removed by you.
31
** 
32
**   0. Attribution Requirement.
33
** 
34
**     You must preserve all legal notices or author attributions in the covered
35
**     work or Appropriate Legal Notices displayed by works containing the covered
36
**     work.  You may not remove from the covered work any author or developer
37
**     credit already included within the covered work.
38
** 
39
**   1. No License To Use Trademarks.
40
** 
41
**     This license does not grant any license or rights to use the trademarks
42
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
43
**     of Golden Code Development Corporation. You are not authorized to use the
44
**     name Golden Code, FWD, or the names of any author or contributor, for
45
**     publicity purposes without written authorization.
46
** 
47
**   2. No Misrepresentation of Affiliation.
48
** 
49
**     You may not represent yourself as Golden Code Development Corporation or FWD.
50
** 
51
**     You may not represent yourself for publicity purposes as associated with
52
**     Golden Code Development Corporation, FWD, or any author or contributor to
53
**     the covered work, without written authorization.
54
** 
55
**   3. No Misrepresentation of Source or Origin.
56
** 
57
**     You may not represent the covered work as solely your work.  All modified
58
**     versions of the covered work must be marked in a reasonable way to make it
59
**     clear that the modified work is not originating from Golden Code Development
60
**     Corporation or FWD.  All modified versions must contain the notices of
61
**     attribution required in this license.
62
*/
63

  
64

  
65
package com.goldencode.p2j.ui;
66

  
67
import java.io.*;
68
import java.lang.invoke.*;
69
import java.lang.reflect.*;
70
import java.nio.file.*;
71
import java.util.*;
72

  
73
import org.objectweb.asm.*;
74
import org.objectweb.asm.Type;
75

  
76
/**
77
 * Generates the bytecode of the {@link ConfigAccessor} for a widget configuration class.
78
 * <p>
79
 * Each operation dispatches the field ID with a {@code tableswitch} to a direct {@code getfield} or
80
 * {@code putfield} instruction, and the bulk operations invoke the per-field operations of the same
81
 * final class, so the JIT can compile them without any dynamic dispatch.
82
 * <p>
83
 * The accessors of the {@link ConfigManager#CONFIG_CLASSES built-in configuration classes} are
84
 * generated at build time by {@link #main}, as regular classes packaged in the FWD jar, so they
85
 * can be included in an AppCDS archive.  The accessors of any other configuration class (external
86
 * widgets) are {@link #define defined} at runtime as hidden classes.
87
 */
88
final class ConfigAccessorGenerator
89
{
90
   /** Internal name of the {@link ConfigAccessor} class. */
91
   private static final String ACCESSOR = Type.getInternalName(ConfigAccessor.class);
92

  
93
   /** Descriptor of the {@link ConfigAccessor#get} method. */
94
   private static final String GET_DESC =
95
      "(Lcom/goldencode/p2j/ui/WidgetConfig;I)Ljava/lang/Object;";
96

  
97
   /** Descriptor of the {@link ConfigAccessor#set} method. */
98
   private static final String SET_DESC =
99
      "(Lcom/goldencode/p2j/ui/WidgetConfig;ILjava/lang/Object;)V";
100

  
101
   /** Descriptor of the {@link ConfigAccessor#same(WidgetConfig, WidgetConfig, int)} method. */
102
   private static final String SAME_DESC =
103
      "(Lcom/goldencode/p2j/ui/WidgetConfig;Lcom/goldencode/p2j/ui/WidgetConfig;I)Z";
104

  
105
   /** Descriptor of the {@link ConfigAccessor#collectChanges} method. */
106
   private static final String COLLECT_DESC =
107
      "(Lcom/goldencode/p2j/ui/WidgetConfig;Lcom/goldencode/p2j/ui/WidgetConfig;[II[Z[I[Ljava/lang/Object;)I";
... This diff was truncated because it exceeds the maximum size that can be displayed.