2525import java .util .LinkedHashMap ;
2626import java .util .List ;
2727import java .util .Map ;
28+ import java .util .Optional ;
29+ import java .util .function .Function ;
2830
31+ import org .apache .commons .lang3 .ObjectUtils ;
2932import org .apache .commons .lang3 .builder .EqualsBuilder ;
3033import org .apache .commons .lang3 .builder .HashCodeBuilder ;
3134import org .xwiki .rendering .block .match .BlockMatcher ;
3235import org .xwiki .rendering .block .match .BlockNavigator ;
3336import org .xwiki .rendering .block .match .CounterBlockMatcher ;
37+ import org .xwiki .rendering .block .match .FunctionBlockMatcher ;
38+ import org .xwiki .rendering .block .match .MetadataBlockMatcher ;
3439import org .xwiki .rendering .listener .Listener ;
40+ import org .xwiki .rendering .listener .MetaData ;
41+ import org .xwiki .rendering .syntax .Syntax ;
3542
3643/**
3744 * Implementation for Block operations. All blocks should extend this class. Supports the notion of generic parameters
@@ -47,6 +54,11 @@ public abstract class AbstractBlock implements Block
4754 */
4855 private Map <String , String > parameters ;
4956
57+ /**
58+ * Store attributes, see {@link #getAttribute(String)} for more explanations what attributes are.
59+ */
60+ private Map <String , Object > attributes ;
61+
5062 /**
5163 * The Blocks this Block contains.
5264 */
@@ -399,6 +411,40 @@ public void setParameters(Map<String, String> parameters)
399411 }
400412 }
401413
414+ @ Override
415+ public Map <String , Object > getAttributes ()
416+ {
417+ return this .attributes == null ? Collections .emptyMap ()
418+ : Collections .unmodifiableMap (this .attributes );
419+ }
420+
421+ @ Override
422+ public Object getAttribute (String name )
423+ {
424+ return this .attributes == null ? null : this .attributes .get (name );
425+ }
426+
427+ @ Override
428+ public void setAttribute (String name , Object value )
429+ {
430+ if (this .attributes == null ) {
431+ this .attributes = new LinkedHashMap <>(1 );
432+ }
433+
434+ this .attributes .put (name , value );
435+ }
436+
437+ @ Override
438+ public void setAttributes (Map <String , Object > attributes )
439+ {
440+ if (this .attributes == null ) {
441+ this .attributes = new LinkedHashMap <>(attributes );
442+ } else {
443+ this .attributes .clear ();
444+ this .attributes .putAll (attributes );
445+ }
446+ }
447+
402448 @ Override
403449 public void setParent (Block parentBlock )
404450 {
@@ -467,6 +513,7 @@ public boolean equals(Object obj)
467513
468514 builder .append (getChildren (), ((Block ) obj ).getChildren ());
469515 builder .append (getParameters (), ((Block ) obj ).getParameters ());
516+ builder .append (getAttributes (), ((Block ) obj ).getAttributes ());
470517
471518 return builder .isEquals ();
472519 }
@@ -481,6 +528,7 @@ public int hashCode()
481528
482529 builder .append (this .childrenBlocks );
483530 builder .append (this .parameters );
531+ builder .append (this .attributes );
484532
485533 return builder .toHashCode ();
486534 }
@@ -511,6 +559,9 @@ public Block clone(BlockFilter blockFilter)
511559 ((AbstractBlock ) block ).parameters = new LinkedHashMap <>(this .parameters );
512560 }
513561
562+ // Clone attribute values if possible as documented in getAttribute().
563+ this .getAttributes ().forEach ((key , value ) -> block .setAttribute (key , ObjectUtils .cloneIfPossible (value )));
564+
514565 if (this .childrenBlocks != null ) {
515566 ((AbstractBlock ) block ).childrenBlocks = new ArrayList <>(this .childrenBlocks .size ());
516567 for (Block childBlock : this .childrenBlocks ) {
@@ -584,4 +635,26 @@ public <T extends Block> T getFirstBlock(BlockMatcher matcher, Axes axes)
584635
585636 return navigator .getFirstBlock (this , axes );
586637 }
638+
639+ @ Override
640+ public Optional <Syntax > getSyntaxMetadata ()
641+ {
642+ MetaDataBlock metaDataBlock = getFirstBlock (MetadataBlockMatcher .SYNTAX , Axes .ANCESTOR_OR_SELF );
643+
644+ if (metaDataBlock != null ) {
645+ return Optional .ofNullable ((Syntax ) metaDataBlock .getMetaData ().getMetaData (MetaData .SYNTAX ));
646+ }
647+
648+ return Optional .empty ();
649+ }
650+
651+ @ Override
652+ public <T > Optional <T > get (Function <Block , Optional <T >> searcher , Axes axes )
653+ {
654+ FunctionBlockMatcher <T > matcher = new FunctionBlockMatcher <>(searcher );
655+
656+ getFirstBlock (matcher , Axes .ANCESTOR_OR_SELF );
657+
658+ return matcher .getValue ();
659+ }
587660}
0 commit comments