Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paragraph - clean up and performance improvements #598

Merged
merged 5 commits into from
Sep 30, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 62 additions & 47 deletions richtextfx/src/main/java/org/fxmisc/richtext/model/Paragraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,19 @@ public final class Paragraph<PS, SEG, S> {

private static <SEG, S> Tuple2<List<SEG>, StyleSpans<S>> decompose(List<StyledSegment<SEG, S>> list,
SegmentOps<SEG, S> segmentOps) {
// TODO: optimize this so that join-able segments/styles are joined before returning tuple
List<SEG> segs = new ArrayList<>();
StyleSpansBuilder<S> builder = new StyleSpansBuilder<>();
for (StyledSegment<SEG, S> styledSegment : list) {
segs.add(styledSegment.getSegment());
// attempt to merge differently-styled consecutive segments into one
int lastIndex = segs.size() - 1;
SEG previousSeg = segs.get(lastIndex);
Optional<SEG> merged = segmentOps.joinSeg(previousSeg, styledSegment.getSegment());
if (merged.isPresent()) {
segs.set(lastIndex, merged.get());
} else {
segs.add(styledSegment.getSegment());
}
// builder merges styles shared between consecutive different segments
builder.add(styledSegment.getStyle(), segmentOps.length(styledSegment.getSegment()));
}
return Tuples.t(segs, builder.create());
Expand All @@ -73,17 +81,14 @@ private static <T> List<T> list(T head, T... tail) {

public Paragraph(PS paragraphStyle, SegmentOps<SEG, S> segmentOps, List<StyledSegment<SEG, S>> styledSegments) {
this(paragraphStyle, segmentOps, decompose(styledSegments, segmentOps));

}

private Paragraph(PS paragraphStyle, SegmentOps<SEG, S> segmentOps, Tuple2<List<SEG>, StyleSpans<S>> decomposedList) {
this(paragraphStyle, segmentOps, decomposedList._1, decomposedList._2);
}

public Paragraph(PS paragraphStyle, SegmentOps<SEG, S> segmentOps, SEG segment, S style) {
this(paragraphStyle, segmentOps, segment,
new StyleSpansBuilder<S>().add(style, segmentOps.length(segment)).create()
);
this(paragraphStyle, segmentOps, segment, StyleSpans.singleton(style, segmentOps.length(segment)));
}

public Paragraph(PS paragraphStyle, SegmentOps<SEG, S> segmentOps, SEG segment, StyleSpans<S> styles) {
Expand All @@ -109,47 +114,16 @@ public Paragraph(PS paragraphStyle, SegmentOps<SEG, S> segmentOps, List<SEG> seg
);
}

public List<StyledSegment<SEG, S>> getStyledSegments() {
if (segments.size() == 1 && styles.getSpanCount() == 1) {
return Collections.singletonList(
new StyledSegment<>(segments.get(0), styles.getStyleSpan(0).getStyle())
);
}
private List<StyledSegment<SEG, S>> styledSegments = null;

List<StyledSegment<SEG, S>> styledSegments = new LinkedList<>();
Iterator<SEG> segIterator = segments.iterator();
Iterator<StyleSpan<S>> styleIterator = styles.iterator();
SEG segCurrent = segIterator.next();
StyleSpan<S> styleCurrent = styleIterator.next();
int segOffset = 0, styleOffset = 0;
boolean finished = false;
while (!finished) {
int segLength = segmentOps.length(segCurrent) - segOffset;
int styleLength = styleCurrent.getLength() - styleOffset;

if (segLength < styleLength) {
SEG splitSeg = segmentOps.subSequence(segCurrent, segOffset);
styledSegments.add(new StyledSegment<>(splitSeg, styleCurrent.getStyle()));
segCurrent = segIterator.next();
segOffset = 0;
styleOffset += segLength;
} else if (styleLength < segLength) {
SEG splitSeg = segmentOps.subSequence(segCurrent, segOffset, segOffset + styleLength);
styledSegments.add(new StyledSegment<>(splitSeg, styleCurrent.getStyle()));
styleCurrent = styleIterator.next();
styleOffset = 0;
segOffset += styleLength;
public List<StyledSegment<SEG, S>> getStyledSegments() {
if (styledSegments == null) {
if (segments.size() == 1 && styles.getSpanCount() == 1) {
styledSegments = Collections.singletonList(
new StyledSegment<>(segments.get(0), styles.getStyleSpan(0).getStyle())
);
} else {
SEG splitSeg = segmentOps.subSequence(segCurrent, segOffset, segOffset + styleLength);
styledSegments.add(new StyledSegment<>(splitSeg, styleCurrent.getStyle()));
if (segIterator.hasNext() && styleIterator.hasNext()) {
segCurrent = segIterator.next();
segOffset = 0;
styleCurrent = styleIterator.next();
styleOffset = 0;
} else {
finished = true;
}
styledSegments = createStyledSegments();
}
}
return styledSegments;
Expand All @@ -164,6 +138,7 @@ public PS getParagraphStyle() {
}

private int length = -1;

public int length() {
if(length == -1) {
length = segments.stream().mapToInt(segmentOps::length).sum();
Expand Down Expand Up @@ -301,8 +276,7 @@ public Paragraph<PS, SEG, S> delete(int start, int end) {
* @return The new paragraph with the restyled segments.
*/
public Paragraph<PS, SEG, S> restyle(S style) {
StyleSpans<S> spans = new StyleSpansBuilder<S>().add(new StyleSpan<>(style, length())).create();
return new Paragraph<>(paragraphStyle, segmentOps, segments, spans);
return new Paragraph<>(paragraphStyle, segmentOps, segments, StyleSpans.singleton(style, length()));
}

public Paragraph<PS, SEG, S> restyle(int from, int to, S style) {
Expand Down Expand Up @@ -401,6 +375,7 @@ public StyleSpans<S> getStyleSpans(int from, int to) {
}

private String text = null;

/**
* Returns the plain text content of this paragraph,
* not including the line terminator.
Expand Down Expand Up @@ -444,4 +419,44 @@ public boolean equals(Object other) {
public int hashCode() {
return Objects.hash(paragraphStyle, segments, styles);
}

private List<StyledSegment<SEG, S>> createStyledSegments() {
List<StyledSegment<SEG, S>> styledSegments = new LinkedList<>();
Iterator<SEG> segIterator = segments.iterator();
Iterator<StyleSpan<S>> styleIterator = styles.iterator();
SEG segCurrent = segIterator.next();
StyleSpan<S> styleCurrent = styleIterator.next();
int segOffset = 0, styleOffset = 0;
boolean finished = false;
while (!finished) {
int segLength = segmentOps.length(segCurrent) - segOffset;
int styleLength = styleCurrent.getLength() - styleOffset;

if (segLength < styleLength) {
SEG splitSeg = segmentOps.subSequence(segCurrent, segOffset);
styledSegments.add(new StyledSegment<>(splitSeg, styleCurrent.getStyle()));
segCurrent = segIterator.next();
segOffset = 0;
styleOffset += segLength;
} else if (styleLength < segLength) {
SEG splitSeg = segmentOps.subSequence(segCurrent, segOffset, segOffset + styleLength);
styledSegments.add(new StyledSegment<>(splitSeg, styleCurrent.getStyle()));
styleCurrent = styleIterator.next();
styleOffset = 0;
segOffset += styleLength;
} else {
SEG splitSeg = segmentOps.subSequence(segCurrent, segOffset, segOffset + styleLength);
styledSegments.add(new StyledSegment<>(splitSeg, styleCurrent.getStyle()));
if (segIterator.hasNext() && styleIterator.hasNext()) {
segCurrent = segIterator.next();
segOffset = 0;
styleCurrent = styleIterator.next();
styleOffset = 0;
} else {
finished = true;
}
}
}
return styledSegments;
}
}