@@ -42,11 +42,19 @@ public final class Paragraph<PS, SEG, S> {
4242
4343 private static <SEG , S > Tuple2 <List <SEG >, StyleSpans <S >> decompose (List <StyledSegment <SEG , S >> list ,
4444 SegmentOps <SEG , S > segmentOps ) {
45- // TODO: optimize this so that join-able segments/styles are joined before returning tuple
4645 List <SEG > segs = new ArrayList <>();
4746 StyleSpansBuilder <S > builder = new StyleSpansBuilder <>();
4847 for (StyledSegment <SEG , S > styledSegment : list ) {
49- segs .add (styledSegment .getSegment ());
48+ // attempt to merge differently-styled consecutive segments into one
49+ int lastIndex = segs .size () - 1 ;
50+ SEG previousSeg = segs .get (lastIndex );
51+ Optional <SEG > merged = segmentOps .joinSeg (previousSeg , styledSegment .getSegment ());
52+ if (merged .isPresent ()) {
53+ segs .set (lastIndex , merged .get ());
54+ } else {
55+ segs .add (styledSegment .getSegment ());
56+ }
57+ // builder merges styles shared between consecutive different segments
5058 builder .add (styledSegment .getStyle (), segmentOps .length (styledSegment .getSegment ()));
5159 }
5260 return Tuples .t (segs , builder .create ());
@@ -73,17 +81,14 @@ private static <T> List<T> list(T head, T... tail) {
7381
7482 public Paragraph (PS paragraphStyle , SegmentOps <SEG , S > segmentOps , List <StyledSegment <SEG , S >> styledSegments ) {
7583 this (paragraphStyle , segmentOps , decompose (styledSegments , segmentOps ));
76-
7784 }
7885
7986 private Paragraph (PS paragraphStyle , SegmentOps <SEG , S > segmentOps , Tuple2 <List <SEG >, StyleSpans <S >> decomposedList ) {
8087 this (paragraphStyle , segmentOps , decomposedList ._1 , decomposedList ._2 );
8188 }
8289
8390 public Paragraph (PS paragraphStyle , SegmentOps <SEG , S > segmentOps , SEG segment , S style ) {
84- this (paragraphStyle , segmentOps , segment ,
85- new StyleSpansBuilder <S >().add (style , segmentOps .length (segment )).create ()
86- );
91+ this (paragraphStyle , segmentOps , segment , StyleSpans .singleton (style , segmentOps .length (segment )));
8792 }
8893
8994 public Paragraph (PS paragraphStyle , SegmentOps <SEG , S > segmentOps , SEG segment , StyleSpans <S > styles ) {
@@ -109,47 +114,16 @@ public Paragraph(PS paragraphStyle, SegmentOps<SEG, S> segmentOps, List<SEG> seg
109114 );
110115 }
111116
112- public List <StyledSegment <SEG , S >> getStyledSegments () {
113- if (segments .size () == 1 && styles .getSpanCount () == 1 ) {
114- return Collections .singletonList (
115- new StyledSegment <>(segments .get (0 ), styles .getStyleSpan (0 ).getStyle ())
116- );
117- }
117+ private List <StyledSegment <SEG , S >> styledSegments = null ;
118118
119- List <StyledSegment <SEG , S >> styledSegments = new LinkedList <>();
120- Iterator <SEG > segIterator = segments .iterator ();
121- Iterator <StyleSpan <S >> styleIterator = styles .iterator ();
122- SEG segCurrent = segIterator .next ();
123- StyleSpan <S > styleCurrent = styleIterator .next ();
124- int segOffset = 0 , styleOffset = 0 ;
125- boolean finished = false ;
126- while (!finished ) {
127- int segLength = segmentOps .length (segCurrent ) - segOffset ;
128- int styleLength = styleCurrent .getLength () - styleOffset ;
129-
130- if (segLength < styleLength ) {
131- SEG splitSeg = segmentOps .subSequence (segCurrent , segOffset );
132- styledSegments .add (new StyledSegment <>(splitSeg , styleCurrent .getStyle ()));
133- segCurrent = segIterator .next ();
134- segOffset = 0 ;
135- styleOffset += segLength ;
136- } else if (styleLength < segLength ) {
137- SEG splitSeg = segmentOps .subSequence (segCurrent , segOffset , segOffset + styleLength );
138- styledSegments .add (new StyledSegment <>(splitSeg , styleCurrent .getStyle ()));
139- styleCurrent = styleIterator .next ();
140- styleOffset = 0 ;
141- segOffset += styleLength ;
119+ public List <StyledSegment <SEG , S >> getStyledSegments () {
120+ if (styledSegments == null ) {
121+ if (segments .size () == 1 && styles .getSpanCount () == 1 ) {
122+ styledSegments = Collections .singletonList (
123+ new StyledSegment <>(segments .get (0 ), styles .getStyleSpan (0 ).getStyle ())
124+ );
142125 } else {
143- SEG splitSeg = segmentOps .subSequence (segCurrent , segOffset , segOffset + styleLength );
144- styledSegments .add (new StyledSegment <>(splitSeg , styleCurrent .getStyle ()));
145- if (segIterator .hasNext () && styleIterator .hasNext ()) {
146- segCurrent = segIterator .next ();
147- segOffset = 0 ;
148- styleCurrent = styleIterator .next ();
149- styleOffset = 0 ;
150- } else {
151- finished = true ;
152- }
126+ styledSegments = createStyledSegments ();
153127 }
154128 }
155129 return styledSegments ;
@@ -164,6 +138,7 @@ public PS getParagraphStyle() {
164138 }
165139
166140 private int length = -1 ;
141+
167142 public int length () {
168143 if (length == -1 ) {
169144 length = segments .stream ().mapToInt (segmentOps ::length ).sum ();
@@ -301,8 +276,7 @@ public Paragraph<PS, SEG, S> delete(int start, int end) {
301276 * @return The new paragraph with the restyled segments.
302277 */
303278 public Paragraph <PS , SEG , S > restyle (S style ) {
304- StyleSpans <S > spans = new StyleSpansBuilder <S >().add (new StyleSpan <>(style , length ())).create ();
305- return new Paragraph <>(paragraphStyle , segmentOps , segments , spans );
279+ return new Paragraph <>(paragraphStyle , segmentOps , segments , StyleSpans .singleton (style , length ()));
306280 }
307281
308282 public Paragraph <PS , SEG , S > restyle (int from , int to , S style ) {
@@ -401,6 +375,7 @@ public StyleSpans<S> getStyleSpans(int from, int to) {
401375 }
402376
403377 private String text = null ;
378+
404379 /**
405380 * Returns the plain text content of this paragraph,
406381 * not including the line terminator.
@@ -444,4 +419,44 @@ public boolean equals(Object other) {
444419 public int hashCode () {
445420 return Objects .hash (paragraphStyle , segments , styles );
446421 }
422+
423+ private List <StyledSegment <SEG , S >> createStyledSegments () {
424+ List <StyledSegment <SEG , S >> styledSegments = new LinkedList <>();
425+ Iterator <SEG > segIterator = segments .iterator ();
426+ Iterator <StyleSpan <S >> styleIterator = styles .iterator ();
427+ SEG segCurrent = segIterator .next ();
428+ StyleSpan <S > styleCurrent = styleIterator .next ();
429+ int segOffset = 0 , styleOffset = 0 ;
430+ boolean finished = false ;
431+ while (!finished ) {
432+ int segLength = segmentOps .length (segCurrent ) - segOffset ;
433+ int styleLength = styleCurrent .getLength () - styleOffset ;
434+
435+ if (segLength < styleLength ) {
436+ SEG splitSeg = segmentOps .subSequence (segCurrent , segOffset );
437+ styledSegments .add (new StyledSegment <>(splitSeg , styleCurrent .getStyle ()));
438+ segCurrent = segIterator .next ();
439+ segOffset = 0 ;
440+ styleOffset += segLength ;
441+ } else if (styleLength < segLength ) {
442+ SEG splitSeg = segmentOps .subSequence (segCurrent , segOffset , segOffset + styleLength );
443+ styledSegments .add (new StyledSegment <>(splitSeg , styleCurrent .getStyle ()));
444+ styleCurrent = styleIterator .next ();
445+ styleOffset = 0 ;
446+ segOffset += styleLength ;
447+ } else {
448+ SEG splitSeg = segmentOps .subSequence (segCurrent , segOffset , segOffset + styleLength );
449+ styledSegments .add (new StyledSegment <>(splitSeg , styleCurrent .getStyle ()));
450+ if (segIterator .hasNext () && styleIterator .hasNext ()) {
451+ segCurrent = segIterator .next ();
452+ segOffset = 0 ;
453+ styleCurrent = styleIterator .next ();
454+ styleOffset = 0 ;
455+ } else {
456+ finished = true ;
457+ }
458+ }
459+ }
460+ return styledSegments ;
461+ }
447462}
0 commit comments