Skip to content

Commit b697414

Browse files
Fix dynamic model crashes from non-standard CSV headers (#4900)
* fix(reflow): harden header normalization against FOAM property name collisions * Remove underscore from fallback field name to avoid CONSTANT_VERSION issues Rename 'field_' + i to 'field' + i so that conversion to constant form (e.g., FIELD0 vs FIELD__0) works cleanly if this ever becomes a static model. * Remove underscores from collision suffixes for CONSTANT_VERSION safety Use 'name2', 'name3' instead of 'name_2', 'name_3' for duplicate column name resolution, consistent with the field fallback change.
1 parent 79d10d6 commit b697414

File tree

1 file changed

+22
-7
lines changed

1 file changed

+22
-7
lines changed

src/foam/core/reflow/Mapping.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ foam.CLASS({
233233
for ( var i = 0 ; i < fileHeaders.length ; i++ ) {
234234
var original = fileHeaders[i];
235235
var normalized = this.normalizeHeader(original);
236+
normalized = normalized || 'field' + i;
236237
normalized = this.resolveConstantCollision(normalized, constantMap);
237238

238239
headerMap[normalized] = original;
@@ -360,29 +361,43 @@ foam.CLASS({
360361
methods: [
361362
function normalizeHeader(header) {
362363
/** Normalize a header string to a valid property name. */
363-
return header.replace(/[^a-zA-Z0-9_]/g, '_').replace(/^[0-9]+/, '');
364+
var s = header.replace(/[^a-zA-Z0-9_]/g, '_').replace(/^[^a-zA-Z]+/, '');
365+
if ( ! s ) return '';
366+
// FOAM property names must start with a lowercase letter to avoid
367+
// collisions with the UPPER_CASE constant name that FOAM generates
368+
// for each property (e.g., an all-caps name would clash with itself).
369+
return s.charAt(0).toLowerCase() + s.substring(1);
364370
},
365371

366372
function resolveConstantCollision(normalized, constantMap) {
367373
/**
368-
* Resolve FOAM constant name collisions by appending a numeric suffix.
369-
* Returns the resolved property name.
374+
* Resolve FOAM constant and property name collisions by appending
375+
* a numeric suffix. constantMap tracks both constant names (keys)
376+
* and used property names (via a '__names__' Set).
377+
* Returns a unique resolved property name.
370378
*/
379+
var names = constantMap.__names__ || ( constantMap.__names__ = {} );
371380
var constantName = foam.String.constantize(normalized);
372381

373-
if ( ! constantMap[constantName] || constantMap[constantName] === normalized ) {
382+
// No collision if both the constant and property name are unused
383+
// or belong to this same normalized name
384+
if ( ( ! constantMap[constantName] || constantMap[constantName] === normalized ) &&
385+
! names[normalized] ) {
374386
constantMap[constantName] = normalized;
387+
names[normalized] = true;
375388
return normalized;
376389
}
377390

378391
// Collision detected - append suffix to make unique
379392
var suffix = 2;
380-
var newNormalized = normalized + '_' + suffix;
381-
while ( constantMap[foam.String.constantize(newNormalized)] ) {
393+
var newNormalized = normalized + suffix;
394+
while ( constantMap[foam.String.constantize(newNormalized)] ||
395+
names[newNormalized] ) {
382396
suffix++;
383-
newNormalized = normalized + '_' + suffix;
397+
newNormalized = normalized + suffix;
384398
}
385399
constantMap[foam.String.constantize(newNormalized)] = newNormalized;
400+
names[newNormalized] = true;
386401
return newNormalized;
387402
},
388403

0 commit comments

Comments
 (0)