Commit 6e02a815 by ibaker Committed by tonihei

Cleanup WebvttCueParser and WebvttCssStyle

- Remove scratchStyleMatches output parameter from WebvttCueParser.
- Switch from String[] to Set<String> for representing classes.
- In-line WebvttCssStyle.reset() since it's not used anywhere else.

PiperOrigin-RevId: 312249552
parent 0de9c007
...@@ -27,8 +27,8 @@ import java.lang.annotation.Retention; ...@@ -27,8 +27,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.HashSet;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import java.util.Set;
/** /**
* Style object of a Css style block in a Webvtt file. * Style object of a Css style block in a Webvtt file.
...@@ -80,7 +80,7 @@ public final class WebvttCssStyle { ...@@ -80,7 +80,7 @@ public final class WebvttCssStyle {
// Selector properties. // Selector properties.
private String targetId; private String targetId;
private String targetTag; private String targetTag;
private List<String> targetClasses; private Set<String> targetClasses;
private String targetVoice; private String targetVoice;
// Style properties. // Style properties.
...@@ -96,18 +96,10 @@ public final class WebvttCssStyle { ...@@ -96,18 +96,10 @@ public final class WebvttCssStyle {
@RubySpan.Position private int rubyPosition; @RubySpan.Position private int rubyPosition;
private boolean combineUpright; private boolean combineUpright;
// Calling reset() is forbidden because `this` isn't initialized. This can be safely suppressed
// because reset() only assigns fields, it doesn't read any.
@SuppressWarnings("nullness:method.invocation.invalid")
public WebvttCssStyle() { public WebvttCssStyle() {
reset();
}
@EnsuresNonNull({"targetId", "targetTag", "targetClasses", "targetVoice"})
public void reset() {
targetId = ""; targetId = "";
targetTag = ""; targetTag = "";
targetClasses = Collections.emptyList(); targetClasses = Collections.emptySet();
targetVoice = ""; targetVoice = "";
fontFamily = null; fontFamily = null;
hasFontColor = false; hasFontColor = false;
...@@ -129,7 +121,7 @@ public final class WebvttCssStyle { ...@@ -129,7 +121,7 @@ public final class WebvttCssStyle {
} }
public void setTargetClasses(String[] targetClasses) { public void setTargetClasses(String[] targetClasses) {
this.targetClasses = Arrays.asList(targetClasses); this.targetClasses = new HashSet<>(Arrays.asList(targetClasses));
} }
public void setTargetVoice(String targetVoice) { public void setTargetVoice(String targetVoice) {
...@@ -155,7 +147,7 @@ public final class WebvttCssStyle { ...@@ -155,7 +147,7 @@ public final class WebvttCssStyle {
* @return The score of the match, zero if there is no match. * @return The score of the match, zero if there is no match.
*/ */
public int getSpecificityScore( public int getSpecificityScore(
@Nullable String id, @Nullable String tag, String[] classes, @Nullable String voice) { @Nullable String id, @Nullable String tag, Set<String> classes, @Nullable String voice) {
if (targetId.isEmpty() && targetTag.isEmpty() && targetClasses.isEmpty() if (targetId.isEmpty() && targetTag.isEmpty() && targetClasses.isEmpty()
&& targetVoice.isEmpty()) { && targetVoice.isEmpty()) {
// The selector is universal. It matches with the minimum score if and only if the given // The selector is universal. It matches with the minimum score if and only if the given
...@@ -166,7 +158,7 @@ public final class WebvttCssStyle { ...@@ -166,7 +158,7 @@ public final class WebvttCssStyle {
score = updateScoreForMatch(score, targetId, id, 0x40000000); score = updateScoreForMatch(score, targetId, id, 0x40000000);
score = updateScoreForMatch(score, targetTag, tag, 2); score = updateScoreForMatch(score, targetTag, tag, 2);
score = updateScoreForMatch(score, targetVoice, voice, 4); score = updateScoreForMatch(score, targetVoice, voice, 4);
if (score == -1 || !Arrays.asList(classes).containsAll(targetClasses)) { if (score == -1 || !classes.containsAll(targetClasses)) {
return 0; return 0;
} else { } else {
score += targetClasses.size() * 4; score += targetClasses.size() * 4;
......
...@@ -49,8 +49,10 @@ import java.util.ArrayList; ...@@ -49,8 +49,10 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...@@ -239,7 +241,6 @@ public final class WebvttCueParser { ...@@ -239,7 +241,6 @@ public final class WebvttCueParser {
@Nullable String id, String markup, List<WebvttCssStyle> styles) { @Nullable String id, String markup, List<WebvttCssStyle> styles) {
SpannableStringBuilder spannedText = new SpannableStringBuilder(); SpannableStringBuilder spannedText = new SpannableStringBuilder();
ArrayDeque<StartTag> startTagStack = new ArrayDeque<>(); ArrayDeque<StartTag> startTagStack = new ArrayDeque<>();
List<StyleMatch> scratchStyleMatches = new ArrayList<>();
int pos = 0; int pos = 0;
List<Element> nestedElements = new ArrayList<>(); List<Element> nestedElements = new ArrayList<>();
while (pos < markup.length()) { while (pos < markup.length()) {
...@@ -270,8 +271,7 @@ public final class WebvttCueParser { ...@@ -270,8 +271,7 @@ public final class WebvttCueParser {
break; break;
} }
startTag = startTagStack.pop(); startTag = startTagStack.pop();
applySpansForTag( applySpansForTag(id, startTag, nestedElements, spannedText, styles);
id, startTag, nestedElements, spannedText, styles, scratchStyleMatches);
if (!startTagStack.isEmpty()) { if (!startTagStack.isEmpty()) {
nestedElements.add(new Element(startTag, spannedText.length())); nestedElements.add(new Element(startTag, spannedText.length()));
} else { } else {
...@@ -307,16 +307,14 @@ public final class WebvttCueParser { ...@@ -307,16 +307,14 @@ public final class WebvttCueParser {
} }
// apply unclosed tags // apply unclosed tags
while (!startTagStack.isEmpty()) { while (!startTagStack.isEmpty()) {
applySpansForTag( applySpansForTag(id, startTagStack.pop(), nestedElements, spannedText, styles);
id, startTagStack.pop(), nestedElements, spannedText, styles, scratchStyleMatches);
} }
applySpansForTag( applySpansForTag(
id, id,
StartTag.buildWholeCueVirtualTag(), StartTag.buildWholeCueVirtualTag(),
/* nestedElements= */ Collections.emptyList(), /* nestedElements= */ Collections.emptyList(),
spannedText, spannedText,
styles, styles);
scratchStyleMatches);
return SpannedString.valueOf(spannedText); return SpannedString.valueOf(spannedText);
} }
...@@ -534,8 +532,7 @@ public final class WebvttCueParser { ...@@ -534,8 +532,7 @@ public final class WebvttCueParser {
StartTag startTag, StartTag startTag,
List<Element> nestedElements, List<Element> nestedElements,
SpannableStringBuilder text, SpannableStringBuilder text,
List<WebvttCssStyle> styles, List<WebvttCssStyle> styles) {
List<StyleMatch> scratchStyleMatches) {
int start = startTag.position; int start = startTag.position;
int end = text.length(); int end = text.length();
...@@ -565,10 +562,9 @@ public final class WebvttCueParser { ...@@ -565,10 +562,9 @@ public final class WebvttCueParser {
return; return;
} }
scratchStyleMatches.clear(); List<StyleMatch> applicableStyles = getApplicableStyles(styles, cueId, startTag);
getApplicableStyles(styles, cueId, startTag, scratchStyleMatches); for (int i = 0; i < applicableStyles.size(); i++) {
for (int i = 0; i < scratchStyleMatches.size(); i++) { applyStyleToText(text, applicableStyles.get(i).style, start, end);
applyStyleToText(text, scratchStyleMatches.get(i).style, start, end);
} }
} }
...@@ -616,8 +612,7 @@ public final class WebvttCueParser { ...@@ -616,8 +612,7 @@ public final class WebvttCueParser {
@RubySpan.Position @RubySpan.Position
private static int getRubyPosition( private static int getRubyPosition(
List<WebvttCssStyle> styles, @Nullable String cueId, StartTag startTag) { List<WebvttCssStyle> styles, @Nullable String cueId, StartTag startTag) {
List<StyleMatch> styleMatches = new ArrayList<>(); List<StyleMatch> styleMatches = getApplicableStyles(styles, cueId, startTag);
getApplicableStyles(styles, cueId, startTag, styleMatches);
for (int i = 0; i < styleMatches.size(); i++) { for (int i = 0; i < styleMatches.size(); i++) {
WebvttCssStyle style = styleMatches.get(i).style; WebvttCssStyle style = styleMatches.get(i).style;
if (style.getRubyPosition() != RubySpan.POSITION_UNKNOWN) { if (style.getRubyPosition() != RubySpan.POSITION_UNKNOWN) {
...@@ -652,7 +647,7 @@ public final class WebvttCueParser { ...@@ -652,7 +647,7 @@ public final class WebvttCueParser {
* colors</a>. * colors</a>.
*/ */
private static void applyDefaultColors( private static void applyDefaultColors(
SpannableStringBuilder text, String[] classes, int start, int end) { SpannableStringBuilder text, Set<String> classes, int start, int end) {
for (String className : classes) { for (String className : classes) {
if (DEFAULT_TEXT_COLORS.containsKey(className)) { if (DEFAULT_TEXT_COLORS.containsKey(className)) {
int color = DEFAULT_TEXT_COLORS.get(className); int color = DEFAULT_TEXT_COLORS.get(className);
...@@ -746,20 +741,18 @@ public final class WebvttCueParser { ...@@ -746,20 +741,18 @@ public final class WebvttCueParser {
return Util.splitAtFirst(tagExpression, "[ \\.]")[0]; return Util.splitAtFirst(tagExpression, "[ \\.]")[0];
} }
private static void getApplicableStyles( private static List<StyleMatch> getApplicableStyles(
List<WebvttCssStyle> declaredStyles, List<WebvttCssStyle> declaredStyles, @Nullable String id, StartTag tag) {
@Nullable String id, List<StyleMatch> applicableStyles = new ArrayList<>();
StartTag tag, for (int i = 0; i < declaredStyles.size(); i++) {
List<StyleMatch> output) {
int styleCount = declaredStyles.size();
for (int i = 0; i < styleCount; i++) {
WebvttCssStyle style = declaredStyles.get(i); WebvttCssStyle style = declaredStyles.get(i);
int score = style.getSpecificityScore(id, tag.name, tag.classes, tag.voice); int score = style.getSpecificityScore(id, tag.name, tag.classes, tag.voice);
if (score > 0) { if (score > 0) {
output.add(new StyleMatch(score, style)); applicableStyles.add(new StyleMatch(score, style));
} }
} }
Collections.sort(output); Collections.sort(applicableStyles);
return applicableStyles;
} }
private static final class WebvttCueInfoBuilder { private static final class WebvttCueInfoBuilder {
...@@ -929,14 +922,12 @@ public final class WebvttCueParser { ...@@ -929,14 +922,12 @@ public final class WebvttCueParser {
private static final class StartTag { private static final class StartTag {
private static final String[] NO_CLASSES = new String[0];
public final String name; public final String name;
public final int position; public final int position;
public final String voice; public final String voice;
public final String[] classes; public final Set<String> classes;
private StartTag(String name, int position, String voice, String[] classes) { private StartTag(String name, int position, String voice, Set<String> classes) {
this.position = position; this.position = position;
this.name = name; this.name = name;
this.voice = voice; this.voice = voice;
...@@ -956,17 +947,19 @@ public final class WebvttCueParser { ...@@ -956,17 +947,19 @@ public final class WebvttCueParser {
} }
String[] nameAndClasses = Util.split(fullTagExpression, "\\."); String[] nameAndClasses = Util.split(fullTagExpression, "\\.");
String name = nameAndClasses[0]; String name = nameAndClasses[0];
String[] classes; Set<String> classes = new HashSet<>();
if (nameAndClasses.length > 1) { for (int i = 1; i < nameAndClasses.length; i++) {
classes = Util.nullSafeArrayCopyOfRange(nameAndClasses, 1, nameAndClasses.length); classes.add(nameAndClasses[i]);
} else {
classes = NO_CLASSES;
} }
return new StartTag(name, position, voice, classes); return new StartTag(name, position, voice, classes);
} }
public static StartTag buildWholeCueVirtualTag() { public static StartTag buildWholeCueVirtualTag() {
return new StartTag("", 0, "", new String[0]); return new StartTag(
/* name= */ "",
/* position= */ 0,
/* voice= */ "",
/* classes= */ Collections.emptySet());
} }
} }
......
...@@ -21,6 +21,9 @@ import static com.google.common.truth.Truth.assertThat; ...@@ -21,6 +21,9 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -163,32 +166,54 @@ public final class CssParserTest { ...@@ -163,32 +166,54 @@ public final class CssParserTest {
public void styleScoreSystem() { public void styleScoreSystem() {
WebvttCssStyle style = new WebvttCssStyle(); WebvttCssStyle style = new WebvttCssStyle();
// Universal selector. // Universal selector.
assertThat(style.getSpecificityScore("", "", new String[0], "")).isEqualTo(1); assertThat(style.getSpecificityScore("", "", Collections.emptySet(), "")).isEqualTo(1);
// Class match without tag match. // Class match without tag match.
style.setTargetClasses(new String[] { "class1", "class2"}); style.setTargetClasses(new String[] { "class1", "class2"});
assertThat(style.getSpecificityScore("", "", new String[]{"class1", "class2", "class3"}, assertThat(
"")).isEqualTo(8); style.getSpecificityScore(
"", "", new HashSet<>(Arrays.asList("class1", "class2", "class3")), ""))
.isEqualTo(8);
// Class and tag match // Class and tag match
style.setTargetTagName("b"); style.setTargetTagName("b");
assertThat(style.getSpecificityScore("", "b", assertThat(
new String[]{"class1", "class2", "class3"}, "")).isEqualTo(10); style.getSpecificityScore(
"", "b", new HashSet<>(Arrays.asList("class1", "class2", "class3")), ""))
.isEqualTo(10);
// Class insufficiency. // Class insufficiency.
assertThat(style.getSpecificityScore("", "b", new String[]{"class1", "class"}, "")) assertThat(
style.getSpecificityScore("", "b", new HashSet<>(Arrays.asList("class1", "class")), ""))
.isEqualTo(0); .isEqualTo(0);
// Voice, classes and tag match. // Voice, classes and tag match.
style.setTargetVoice("Manuel Cráneo"); style.setTargetVoice("Manuel Cráneo");
assertThat(style.getSpecificityScore("", "b", assertThat(
new String[]{"class1", "class2", "class3"}, "Manuel Cráneo")).isEqualTo(14); style.getSpecificityScore(
"",
"b",
new HashSet<>(Arrays.asList("class1", "class2", "class3")),
"Manuel Cráneo"))
.isEqualTo(14);
// Voice mismatch. // Voice mismatch.
assertThat(style.getSpecificityScore(null, "b", assertThat(
new String[]{"class1", "class2", "class3"}, "Manuel Craneo")).isEqualTo(0); style.getSpecificityScore(
null,
"b",
new HashSet<>(Arrays.asList("class1", "class2", "class3")),
"Manuel Craneo"))
.isEqualTo(0);
// Id, voice, classes and tag match. // Id, voice, classes and tag match.
style.setTargetId("id"); style.setTargetId("id");
assertThat(style.getSpecificityScore("id", "b", assertThat(
new String[]{"class1", "class2", "class3"}, "Manuel Cráneo")).isEqualTo(0x40000000 + 14); style.getSpecificityScore(
"id",
"b",
new HashSet<>(Arrays.asList("class1", "class2", "class3")),
"Manuel Cráneo"))
.isEqualTo(0x40000000 + 14);
// Id mismatch. // Id mismatch.
assertThat(style.getSpecificityScore("id1", "b", assertThat(
new String[]{"class1", "class2", "class3"}, "")).isEqualTo(0); style.getSpecificityScore(
"id1", "b", new HashSet<>(Arrays.asList("class1", "class2", "class3")), ""))
.isEqualTo(0);
} }
// Utility methods. // Utility methods.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment