@@ -145,7 +145,7 @@ public final class ComponentImpl extends StructSupport implements Externalizable
145145 ComponentImpl top = this ;
146146 ComponentImpl base ;
147147 private PageSource pageSource ;
148- private ComponentPageRef cpRef ;
148+ private ComponentPageImpl cp ;
149149 private ComponentScope scope ;
150150
151151 // for all the same
@@ -179,7 +179,8 @@ public final class ComponentImpl extends StructSupport implements Externalizable
179179 /**
180180 * Constructor of the Component, USED ONLY FOR DESERIALIZE
181181 */
182- public ComponentImpl () {}
182+ public ComponentImpl () {
183+ }
183184
184185 /**
185186 * constructor of the class
@@ -207,7 +208,7 @@ public ComponentImpl(ComponentPageImpl componentPage, Boolean output, boolean _s
207208 this .properties = new ComponentProperties (componentPage .getComponentName (), dspName , extend .trim (), implement , hint , output , callPath + appendix , realPath ,
208209 componentPage .getSubname (), _synchronized , null , persistent , accessors , modifier , meta );
209210
210- this .cpRef = new ComponentPageRef ( componentPage ) ;
211+ this .cp = componentPage ;
211212 this .pageSource = componentPage .getPageSource ();
212213 this .importDefintions = componentPage .getImportDefintions ();
213214 // if(modifier!=0)
@@ -217,17 +218,11 @@ public ComponentImpl(ComponentPageImpl componentPage, Boolean output, boolean _s
217218 }
218219
219220 public JavaSettings getJavaSettings (PageContext pc ) throws IOException {
220- ComponentPageImpl cp ;
221- try {
222- cp = this .cpRef .get (pc );
223- }
224- catch (PageException e ) {
225- throw ExceptionUtil .toIOException (e );
226- }
227- boolean is = cp .isJavaSettingsInitialized ();
221+
222+ boolean is = this .cp .isJavaSettingsInitialized ();
228223 if (!is ) {
229224 synchronized (cp ) {
230- is = cp .isJavaSettingsInitialized ();
225+ is = this . cp .isJavaSettingsInitialized ();
231226 if (!is ) {
232227 boolean mergeConfig = false ;
233228 JavaSettings js = null ;
@@ -270,18 +265,12 @@ public JavaSettings getJavaSettings(PageContext pc) throws IOException {
270265 // current
271266 js = JavaSettingsImpl .merge (pc .getConfig (), js , JavaSettingsImpl .readJavaSettings (pc , properties .meta ));
272267 if (mergeConfig ) js = JavaSettingsImpl .merge (pc .getConfig (), ((ConfigPro ) pc .getConfig ()).getJavaSettings (), js );
273- ;
274268
275- return cp .setJavaSettings (js );
269+ return this . cp .setJavaSettings (js );
276270 }
277271 }
278272 }
279- return cp .getJavaSettings ();
280- }
281-
282- @ Override
283- public final int hashCode () {
284- return java .util .Objects .hash (base , _data , pageSource );
273+ return this .cp .getJavaSettings ();
285274 }
286275
287276 public boolean hasJavaSettings (PageContext pc ) {
@@ -311,7 +300,7 @@ public ComponentImpl _duplicate(boolean deepCopy, boolean isTop) {
311300 try {
312301 // attributes
313302 trg .pageSource = pageSource ;
314- trg .cpRef = cpRef ;
303+ trg .cp = cp ;
315304 // trg._triggerDataMember=_triggerDataMember;
316305 trg .useShadow = useShadow ;
317306 trg ._static = _static ;
@@ -485,7 +474,7 @@ public void init(PageContext pageContext, ComponentPageImpl componentPage, boole
485474
486475 if (base != null ) {
487476 this .dataMemberDefaultAccess = base .dataMemberDefaultAccess ;
488- this ._static = new StaticScope (base ._static , this , cpRef , dataMemberDefaultAccess );
477+ this ._static = new StaticScope (base ._static , this , new ComponentPageRef ( componentPage ) , dataMemberDefaultAccess );
489478 this .absFin = base .absFin ;
490479 _data = base ._data ;
491480 _udfs = isRestEnabled ? new LinkedHashMap <Key , UDF >(base ._udfs ) : new HashMap <Key , UDF >(base ._udfs );
@@ -496,7 +485,7 @@ public void init(PageContext pageContext, ComponentPageImpl componentPage, boole
496485 }
497486 else {
498487 this .dataMemberDefaultAccess = pageContext .getConfig ().getComponentDataMemberDefaultAccess ();
499- this ._static = new StaticScope (null , this , cpRef , dataMemberDefaultAccess );
488+ this ._static = new StaticScope (null , this , new ComponentPageRef ( componentPage ) , dataMemberDefaultAccess );
500489 // TODO get per CFC setting
501490 // this._triggerDataMember=pageContext.getConfig().getTriggerComponentDataMember();
502491 _udfs = isRestEnabled ? new LinkedHashMap <Key , UDF >() : new HashMap <Key , UDF >();
@@ -511,7 +500,7 @@ public void init(PageContext pageContext, ComponentPageImpl componentPage, boole
511500
512501 long indexBase = 0 ;
513502 if (base != null ) {
514- indexBase = base .cpRef . get ( pageContext ) .getStaticStruct ().index ();
503+ indexBase = base .cp .getStaticStruct ().index ();
515504 }
516505
517506 // scope
@@ -759,8 +748,7 @@ else if (_namedArgs != null) {
759748 return Reflector .componentToClass (pc , this ).getClass ();
760749 }
761750
762- // When calling via super, use public access for error message since super calls should access
763- // inherited methods
751+ // When calling via super, use public access for error message since super calls should access inherited methods
764752 int errorAccess = superAccess ? ACCESS_PUBLIC : access ;
765753 if (member == null ) throw ComponentUtil .notFunction (this , KeyImpl .init (name ), null , errorAccess );
766754 throw ComponentUtil .notFunction (this , KeyImpl .init (name ), member .getValue (), errorAccess );
@@ -990,21 +978,21 @@ private Collection.Key[] keysPreservingOrder(int access) {
990978 if (_udfs .isEmpty () && _data .isEmpty ()) {
991979 return new Collection .Key [0 ];
992980 }
993-
981+
994982 List <Key > orderedKeys = new ArrayList <Key >(_udfs .size () + _data .size ());
995-
996- for (Entry <Key , UDF > entry : _udfs .entrySet ()) {
983+
984+ for (Entry <Key , UDF > entry : _udfs .entrySet ()) {
997985 if (entry .getValue ().getAccess () <= access ) {
998986 orderedKeys .add (entry .getKey ());
999987 }
1000988 }
1001- for (Entry <Key , Member > entry : _data .entrySet ()) {
989+ for (Entry <Key , Member > entry : _data .entrySet ()) {
1002990 Member member = entry .getValue ();
1003991 if (member .getAccess () <= access && !(member instanceof UDF )) {
1004992 orderedKeys .add (entry .getKey ());
1005993 }
1006994 }
1007-
995+
1008996 return orderedKeys .toArray (new Collection .Key [orderedKeys .size ()]);
1009997 }
1010998
@@ -1194,7 +1182,8 @@ public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties
11941182 try {
11951183 return DumpUtil .toDumpData (_call (pageContext , KeyConstants .__toDumpData , udf , null , new Object [0 ]), pageContext , maxlevel , dp );
11961184 }
1197- catch (PageException e ) {}
1185+ catch (PageException e ) {
1186+ }
11981187 }
11991188 }
12001189 }
@@ -1392,8 +1381,8 @@ public PageSource _getPageSource() {
13921381 return pageSource ;
13931382 }
13941383
1395- public ComponentPageImpl _getComponentPageImpl (PageContext pc ) throws PageException {
1396- return cpRef . get ( pc ) ;
1384+ public ComponentPageImpl _getComponentPageImpl () {
1385+ return cp ;
13971386 }
13981387
13991388 public ImportDefintion [] _getImportDefintions () {
@@ -1728,7 +1717,7 @@ protected static Struct getMetaData(int access, PageContext pc, ComponentImpl co
17281717 if (!StringUtil .isEmpty (displayname )) sct .set (KeyConstants ._displayname , displayname );
17291718
17301719 sct .set (KeyConstants ._persistent , comp .properties .persistent );
1731- // sct.set(KeyConstants._hashCode, comp.hashCode());
1720+ sct .set (KeyConstants ._hashCode , comp .hashCode ());
17321721 sct .set (KeyConstants ._accessors , comp .properties .accessors );
17331722 sct .set (KeyConstants ._synchronized , comp .properties ._synchronized );
17341723 sct .set (KeyConstants ._inline , comp .properties .inline );
@@ -1988,14 +1977,9 @@ private Object _set(PageContext pc, Collection.Key key, Object value, int access
19881977 "enable [trigger data member] in administrator to also invoke getters and setters" );
19891978 if (existing != null ) {
19901979 if (existing .getModifier () == Member .MODIFIER_FINAL ) {
1991- ComponentPageImpl tmp = cpRef .get (pc , null );
1992- String componentName = tmp != null ? tmp .getComponentName () : "" ;
1993-
1994- tmp = base .cpRef .get (pc , null );
1995- String baseComponentName = tmp != null ? tmp .getComponentName () : "" ;
1996-
1997- throw new ExpressionException ("Attempt to modify a 'final' member [" + key + "] within the 'this' scope of the component [" + componentName
1998- + "]. This member is declared as 'final' in the base component [" + baseComponentName + "] or a component extended by it, and cannot be overridden." );
1980+ throw new ExpressionException ("Attempt to modify a 'final' member [" + key + "] within the 'this' scope of the component [" + cp .getComponentName ()
1981+ + "]. This member is declared as 'final' in the base component [" + base .cp .getComponentName ()
1982+ + "] or a component extended by it, and cannot be overridden." );
19991983 }
20001984
20011985 }
@@ -2404,16 +2388,65 @@ public boolean isAccessors() {
24042388
24052389 @ Override
24062390 public void setProperty (Property property ) throws PageException {
2407- top .properties .properties .put (StringUtil .toLowerCase (property .getName ()), property );
2408- // FUTURE getDefaultAsObject was added in Beta pahse of Lucee 7, so we keep the checkcast in place
2409- if (((PropertyImpl ) property ).getDefaultAsObject () != null ) scope .setEL (KeyImpl .init (property .getName ()), ((PropertyImpl ) property ).getDefaultAsObject ());
2410- if (top .properties .persistent || top .properties .accessors ) {
2411- PropertyFactory .createPropertyUDFs (this , property );
2391+ // LDEV-3335: Handle property inheritance and overrides
2392+ PropertyImpl propImpl = (PropertyImpl ) property ;
2393+ PageSource propOwnerPS = propImpl .getOwnerPageSource ();
2394+
2395+ // Check if this property is overriding an existing property (same name already registered)
2396+ String propNameLower = StringUtil .toLowerCase (propImpl .getName ());
2397+ PropertyImpl existing = (PropertyImpl ) top .properties .properties .get (propNameLower );
2398+ boolean isOverride = existing != null && propOwnerPS == null ;
2399+
2400+ boolean isInherited = propOwnerPS != null && !propOwnerPS .equals (getPageSource ());
2401+
2402+ if (isInherited && !isOverride ) {
2403+ // Property is from a parent component - duplicate it to avoid sharing/mutation
2404+ propImpl = (PropertyImpl ) propImpl .duplicate (false );
2405+ }
2406+ else if (propOwnerPS == null ) {
2407+ // LDEV-3335: Property doesn't have owner set yet - set it to this component
2408+ // This happens for properties from __staticProperties that haven't been initialized
2409+ propImpl .setOwnerName (getAbsName (), getPageSource ());
2410+ }
2411+
2412+ top .properties .properties .put (propNameLower , propImpl );
2413+ if (propImpl .getDefaultAsObject () != null ) {
2414+ scope .setEL (propImpl .getNameAsKey (), propImpl .getDefaultAsObject ());
2415+ }
2416+ // Create accessor UDFs if:
2417+ // 1. Component has accessors enabled, OR
2418+ // 2. Component is persistent, OR
2419+ // 3. Property is inherited and has accessors (need to create new UDFs with duplicated property)
2420+ // 4. Property is an override with accessors (child re-declaring parent property)
2421+ if (top .properties .persistent || top .properties .accessors || (isInherited && (propImpl .getGetter () || propImpl .getSetter ()))
2422+ || (isOverride && (propImpl .getGetter () || propImpl .getSetter ()))) {
2423+ PropertyFactory .createPropertyUDFs (this , propImpl );
24122424 }
24132425 }
24142426
24152427 private void initProperties () throws PageException {
24162428 top .properties .properties = new LinkedHashMap <String , Property >();
2429+ // Call generated stub to initialize properties from static registry (zero overhead!)
2430+ if (top .cp != null ) {
2431+ top .cp .initPropertiesStub (this );
2432+ }
2433+
2434+ // LDEV-3335: Add static flyweight accessor UDFs to _data and scope
2435+ Map <Key , UDF > staticAccessorUDFs = top .cp != null ? top .cp .getStaticAccessorUDFs () : null ;
2436+ if (staticAccessorUDFs != null && !staticAccessorUDFs .isEmpty ()) {
2437+ Iterator <Map .Entry <Key , UDF >> it = staticAccessorUDFs .entrySet ().iterator ();
2438+ while (it .hasNext ()) {
2439+ Map .Entry <Key , UDF > entry = it .next ();
2440+ Key key = entry .getKey ();
2441+ UDF udf = entry .getValue ();
2442+
2443+ // Only add if not manually overridden
2444+ if (!_data .containsKey (key )) {
2445+ _data .put (key , udf );
2446+ scope .put (key , udf );
2447+ }
2448+ }
2449+ }
24172450
24182451 // MappedSuperClass
24192452 if (isPersistent () && !isBasePeristent () && top .base != null && top .base .properties .properties != null && top .base .properties .meta != null ) {
@@ -2424,8 +2457,11 @@ private void initProperties() throws PageException {
24242457 while (it .hasNext ()) {
24252458 p = it .next ().getValue ();
24262459 if (p .isPeristent ()) {
2427-
2428- setProperty (p );
2460+ // LDEV-87: Don't override properties that child component has already declared
2461+ String propNameLower = StringUtil .toLowerCase (p .getName ());
2462+ if (!top .properties .properties .containsKey (propNameLower )) {
2463+ setProperty (p );
2464+ }
24292465 }
24302466 }
24312467 }
0 commit comments