Add CSS Specificity score system to WebvttCssStyle

This CL provides the necessary infrastructure to add styling by class. This was separated
into two different CLs to ease reviewing.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=120336976
This commit is contained in:
aquilescanta 2016-04-20 07:59:34 -07:00 committed by Oliver Woodman
parent 0fc53f6e37
commit c11fda04e5
3 changed files with 106 additions and 1 deletions

View File

@ -178,6 +178,36 @@ public final class CssParserTest extends InstrumentationTestCase {
assertEquals(CssParser.parseNextToken(input, builder), null);
}
public void testStyleScoreSystem() {
WebvttCssStyle style = new WebvttCssStyle();
// Universal selector.
assertEquals(1, style.getSpecificityScore(null, null, new String[0], null));
// Class match without tag match.
style.setTargetClasses(new String[] { "class1", "class2"});
assertEquals(8, style.getSpecificityScore(null, null,
new String[] { "class1", "class2", "class3" }, null));
// Class and tag match
style.setTargetTagName("b");
assertEquals(10, style.getSpecificityScore(null, "b",
new String[] { "class1", "class2", "class3" }, null));
// Class insufficiency.
assertEquals(0, style.getSpecificityScore(null, "b", new String[] { "class1", "class" }, null));
// Voice, classes and tag match.
style.setTargetVoice("Manuel Cráneo");
assertEquals(14, style.getSpecificityScore(null, "b",
new String[] { "class1", "class2", "class3" }, "Manuel Cráneo"));
// Voice mismatch.
assertEquals(0, style.getSpecificityScore(null, "b",
new String[] { "class1", "class2", "class3" }, "Manuel Craneo"));
// Id, voice, classes and tag match.
style.setTargetId("id");
assertEquals(0x40000000 + 14, style.getSpecificityScore("id", "b",
new String[] { "class1", "class2", "class3" }, "Manuel Cráneo"));
// Id mismatch.
assertEquals(0, style.getSpecificityScore("id1", "b",
new String[] { "class1", "class2", "class3" }, null));
}
// Utility methods.
private void assertSkipsToEndOfSkip(String expectedLine, String s) {

View File

@ -20,6 +20,10 @@ import com.google.android.exoplayer.util.Util;
import android.graphics.Typeface;
import android.text.Layout;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Style object of a Css style block in a Webvtt file.
*
@ -42,6 +46,13 @@ import android.text.Layout;
private static final int OFF = 0;
private static final int ON = 1;
// Selector properties.
private String targetId;
private String targetTag;
private List<String> targetClasses;
private String targetVoice;
// Style properties.
private String fontFamily;
private int fontColor;
private boolean hasFontColor;
@ -60,6 +71,10 @@ import android.text.Layout;
}
public void reset() {
targetId = "";
targetTag = "";
targetClasses = Collections.emptyList();
targetVoice = "";
fontFamily = null;
hasFontColor = false;
hasBackgroundColor = false;
@ -71,6 +86,59 @@ import android.text.Layout;
textAlign = null;
}
public void setTargetId(String targetId) {
this.targetId = targetId;
}
public void setTargetTagName(String targetTag) {
this.targetTag = targetTag;
}
public void setTargetClasses(String[] targetClasses) {
this.targetClasses = Arrays.asList(targetClasses);
}
public void setTargetVoice(String targetVoice) {
this.targetVoice = targetVoice;
}
/**
* Returns a value in a score system compliant with the CSS Specificity rules.
*
* @see <a href="https://www.w3.org/TR/CSS2/cascade.html">CSS Cascading</a>
*
* The score works as follows:
* <ul>
* <li> Id match adds 0x40000000 to the score.
* <li> Each class and voice match adds 4 to the score.
* <li> Tag matching adds 2 to the score.
* <li> Universal selector matching scores 1.
* </ul>
*
* @param id The id of the cue if present, {@code null} otherwise.
* @param tag Name of the tag, {@code null} if it refers to the entire cue.
* @param classes An array containing the classes the tag belongs to. Must not be null.
* @param voice Annotated voice if present, {@code null} otherwise.
* @return The score of the match, zero if there is no match.
*/
public int getSpecificityScore(String id, String tag, String[] classes, String voice) {
if (targetId.isEmpty() && targetTag.isEmpty() && targetClasses.isEmpty()
&& targetVoice.isEmpty()) {
// The selector is universal. It matches with the minimum score.
return 1;
}
int score = 0;
score = updateScoreForMatch(score, targetId, id, 0x40000000);
score = updateScoreForMatch(score, targetTag, tag, 2);
score = updateScoreForMatch(score, targetVoice, voice, 4);
if (score == -1 || !Arrays.asList(classes).containsAll(targetClasses)) {
return 0;
} else {
score += targetClasses.size() * 4;
}
return score;
}
/**
* Returns the style or {@link #UNSPECIFIED} when no style information is given.
*
@ -214,5 +282,12 @@ import android.text.Layout;
}
}
private static int updateScoreForMatch(int currentScore, String target, String actual,
int score) {
if (target.isEmpty() || currentScore == -1) {
return currentScore;
}
return target.equals(actual) ? currentScore + score : -1;
}
}

View File

@ -211,10 +211,10 @@ import java.util.regex.Pattern;
}
}
// apply unclosed tags
applyStyleToText(spannedText, styleMap.get(UNIVERSAL_CUE_ID), 0, spannedText.length());
while (!startTagStack.isEmpty()) {
applySpansForTag(startTagStack.pop(), spannedText, styleMap);
}
applyStyleToText(spannedText, styleMap.get(UNIVERSAL_CUE_ID), 0, spannedText.length());
applyStyleToText(spannedText, styleMap.get(CUE_ID_PREFIX + id), 0, spannedText.length());
builder.setText(spannedText);
}