From fb6339d0754981d667847be9633972b56f472e05 Mon Sep 17 00:00:00 2001 From: Haijian Wang Date: Wed, 13 Feb 2013 14:51:18 +0200 Subject: [PATCH] Sass replaces variables based on substring matches. (Tickets #10508, #10730) Change-Id: I7d3c5a74055e28e0e22fe6e496209d7d3c42b4af --- .../sass/internal/tree/FunctionNode.java | 6 ++- .../sass/internal/tree/MicrosoftRuleNode.java | 7 +-- .../internal/tree/NestPropertiesNode.java | 7 ++- .../vaadin/sass/internal/tree/RuleNode.java | 27 +++------- .../vaadin/sass/internal/tree/SimpleNode.java | 7 +-- .../sass/internal/tree/VariableNode.java | 8 +-- .../tree/controldirective/IfNode.java | 9 ++-- .../vaadin/sass/internal/util/StringUtil.java | 43 +++++++++++++++ .../automatic/css/var-in-css-function.css | 4 ++ .../automatic/css/var-substring-match.css | 3 ++ .../automatic/scss/var-in-css-function.scss | 15 ++++++ .../automatic/scss/var-substring-match.scss | 8 +++ .../sass/internal/util/StringUtilTest.java | 53 +++++++++++++++++++ 13 files changed, 157 insertions(+), 40 deletions(-) create mode 100644 theme-compiler/tests/resources/automatic/css/var-in-css-function.css create mode 100644 theme-compiler/tests/resources/automatic/css/var-substring-match.css create mode 100644 theme-compiler/tests/resources/automatic/scss/var-in-css-function.scss create mode 100644 theme-compiler/tests/resources/automatic/scss/var-substring-match.scss create mode 100644 theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/FunctionNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/FunctionNode.java index 919db1fdd7..ced4671051 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/FunctionNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/FunctionNode.java @@ -19,6 +19,7 @@ package com.vaadin.sass.internal.tree; import java.util.ArrayList; import com.vaadin.sass.internal.ScssStylesheet; +import com.vaadin.sass.internal.util.StringUtil; public class FunctionNode extends Node implements IVariableNode { private static final long serialVersionUID = -5383104165955523923L; @@ -47,8 +48,9 @@ public class FunctionNode extends Node implements IVariableNode { @Override public void replaceVariables(ArrayList variables) { for (final VariableNode node : variables) { - if (args.contains(node.getName())) { - args.replaceAll(node.getName(), node.getExpr().toString()); + if (StringUtil.containsVariable(args, node.getName())) { + args = StringUtil.replaceVariable(args, node.getName(), node + .getExpr().toString()); } } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/MicrosoftRuleNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/MicrosoftRuleNode.java index 1644c1c336..3938188a72 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/MicrosoftRuleNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/MicrosoftRuleNode.java @@ -18,6 +18,7 @@ package com.vaadin.sass.internal.tree; import java.util.ArrayList; import com.vaadin.sass.internal.ScssStylesheet; +import com.vaadin.sass.internal.util.StringUtil; public class MicrosoftRuleNode extends Node implements IVariableNode { @@ -32,9 +33,9 @@ public class MicrosoftRuleNode extends Node implements IVariableNode { @Override public void replaceVariables(ArrayList variables) { for (final VariableNode var : variables) { - if (value.contains("$" + var.getName())) { - value = value.replaceAll("$" + var.getName(), var.getExpr() - .toString()); + if (StringUtil.containsVariable(value, var.getName())) { + value = StringUtil.replaceVariable(value, var.getName(), var + .getExpr().toString()); } } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/NestPropertiesNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/NestPropertiesNode.java index 63a42034bf..fc50cfda61 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/NestPropertiesNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/NestPropertiesNode.java @@ -59,10 +59,9 @@ public class NestPropertiesNode extends Node implements IVariableNode { @Override public void replaceVariables(ArrayList variables) { - for (final VariableNode node : variables) { - if (name.contains(node.getName())) { - name = name.replaceAll(node.getName(), node.getExpr() - .toString()); + for (Node child : getChildren()) { + if (child instanceof RuleNode) { + ((RuleNode) child).replaceVariables(variables); } } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java index 73ab31b4a1..a78d9d66d2 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java @@ -21,6 +21,7 @@ import java.util.regex.Pattern; import com.vaadin.sass.internal.ScssStylesheet; import com.vaadin.sass.internal.parser.LexicalUnitImpl; +import com.vaadin.sass.internal.util.StringUtil; public class RuleNode extends Node implements IVariableNode { private static final long serialVersionUID = 6653493127869037022L; @@ -95,28 +96,14 @@ public class RuleNode extends Node implements IVariableNode { if (value.getLexicalUnitType() == LexicalUnitImpl.SAC_FUNCTION) { if (value.getParameters() != null) { - if (value.getParameters().toString() - .contains(node.getName())) { - + if (StringUtil.containsVariable(value.getParameters() + .toString(), node.getName())) { LexicalUnitImpl param = value.getParameters(); while (param != null) { - if (param.getValue().toString() - .contains(node.getName())) { - - String value = node.getExpr().toString(); - - LexicalUnitImpl prev = param - .getPreviousLexicalUnit(); - LexicalUnitImpl next = param - .getNextLexicalUnit(); - - if (param.getLexicalUnitType() == LexicalUnitImpl.SCSS_VARIABLE) { - param.setStringValue(value); - param.setLexicalUnitType(node.getExpr() - .getLexicalUnitType()); - param.setPrevLexicalUnit(prev); - param.setNextLexicalUnit(next); - } + if (param.getLexicalUnitType() == LexicalUnitImpl.SCSS_VARIABLE + && param.getValue().toString() + .equals(node.getName())) { + param.replaceValue(node.getExpr()); } param = param.getNextLexicalUnit(); } diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/SimpleNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/SimpleNode.java index cb27498562..796f4d8d1d 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/SimpleNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/SimpleNode.java @@ -18,6 +18,7 @@ package com.vaadin.sass.internal.tree; import java.util.ArrayList; import com.vaadin.sass.internal.ScssStylesheet; +import com.vaadin.sass.internal.util.StringUtil; /** * A simple BlockNode where input text equals output. Note : ignores any @@ -44,9 +45,9 @@ public class SimpleNode extends Node implements IVariableNode { @Override public void replaceVariables(ArrayList variables) { for (final VariableNode node : variables) { - if (text.contains(node.getName())) { - text = text.replaceAll(node.getName(), node.getExpr() - .toString()); + if (StringUtil.containsVariable(text, node.getName())) { + text = StringUtil.replaceVariable(text, node.getName(), node + .getExpr().toString()); } } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java index f9b6f9dc8e..90be727f88 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import com.vaadin.sass.internal.ScssStylesheet; import com.vaadin.sass.internal.parser.LexicalUnitImpl; +import com.vaadin.sass.internal.util.StringUtil; import com.vaadin.sass.internal.visitor.VariableNodeHandler; public class VariableNode extends Node implements IVariableNode { @@ -72,10 +73,11 @@ public class VariableNode extends Node implements IVariableNode { for (final VariableNode node : variables) { if (!equals(node)) { - if (expr.toString().contains("$" + node.getName())) { + if (StringUtil + .containsVariable(expr.toString(), node.getName())) { if (expr.getParameters() != null - && expr.getParameters().toString() - .contains("$" + node.getName())) { + && StringUtil.containsVariable(expr.getParameters() + .toString(), node.getName())) { replaceValues(expr.getParameters(), node); } else if (expr.getLexicalUnitType() == LexicalUnitImpl.SCSS_VARIABLE) { replaceValues(expr, node); diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/controldirective/IfNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/controldirective/IfNode.java index de8a5a8551..af795a58c3 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/controldirective/IfNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/controldirective/IfNode.java @@ -16,12 +16,12 @@ package com.vaadin.sass.internal.tree.controldirective; import java.util.ArrayList; -import java.util.regex.Pattern; import com.vaadin.sass.internal.ScssStylesheet; import com.vaadin.sass.internal.tree.IVariableNode; import com.vaadin.sass.internal.tree.Node; import com.vaadin.sass.internal.tree.VariableNode; +import com.vaadin.sass.internal.util.StringUtil; public class IfNode extends Node implements IfElseNode, IVariableNode { private String expression; @@ -47,10 +47,9 @@ public class IfNode extends Node implements IfElseNode, IVariableNode { @Override public void replaceVariables(ArrayList variables) { for (final VariableNode node : variables) { - String variable = "$" + node.getName(); - if (expression.contains(variable)) { - expression = expression.replaceAll(Pattern.quote(variable), - node.getExpr().toString()); + if (StringUtil.containsVariable(expression, node.getName())) { + expression = StringUtil.replaceVariable(expression, + node.getName(), node.getExpr().toString()); } } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java b/theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java index dcfd3db5c6..cf227fe3a3 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java +++ b/theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class StringUtil { private static final String FOLDER_SEPARATOR = "/"; // folder separator @@ -133,4 +135,45 @@ public class StringUtil { String delim) { return collectionToDelimitedString(coll, delim, "", ""); } + + /** + * Check if a String contains a SCSS variable, using whole word match. + * + * @param text + * text to be checked + * @Param varName SCSS variable name to be checked. (Without '$' sign) + * @return true if the text contains the SCSS variable, false if not + */ + public static boolean containsVariable(String text, String varName) { + StringBuilder builder = new StringBuilder(); + // (?![\\w-]) means lookahead, the next one shouldn't be a word + // character nor a dash. + builder.append("\\$").append(Pattern.quote(varName)) + .append("(?![\\w-])"); + Pattern pattern = Pattern.compile(builder.toString()); + Matcher matcher = pattern.matcher(text); + return matcher.find(); + } + + /** + * Replace the SCSS variable in a String to its corresponding value, using + * whole word match. + * + * @param text + * text which contains the SCSS variable + * @param varName + * SCSS variable name + * @param value + * the value of the SCSS variable + * @return the String after replacing + */ + public static String replaceVariable(String text, String varName, + String value) { + StringBuilder builder = new StringBuilder(); + // (?![\\w-]) means lookahead, the next one shouldn't be a word + // character nor a dash. + builder.append("\\$").append(Pattern.quote(varName)) + .append("(?![\\w-])"); + return text.replaceAll(builder.toString(), value); + } } diff --git a/theme-compiler/tests/resources/automatic/css/var-in-css-function.css b/theme-compiler/tests/resources/automatic/css/var-in-css-function.css new file mode 100644 index 0000000000..07a921678d --- /dev/null +++ b/theme-compiler/tests/resources/automatic/css/var-in-css-function.css @@ -0,0 +1,4 @@ +.v-window-footer { + background: linear-gradient(bottom, hsl(110, 50%, 98%), hsl(110, 50%, 90%)); + abc: rgba(rgb(0, 255, 13), 0.85); +} \ No newline at end of file diff --git a/theme-compiler/tests/resources/automatic/css/var-substring-match.css b/theme-compiler/tests/resources/automatic/css/var-substring-match.css new file mode 100644 index 0000000000..54c97eac63 --- /dev/null +++ b/theme-compiler/tests/resources/automatic/css/var-substring-match.css @@ -0,0 +1,3 @@ +.foo { + font-size: 10px; +} \ No newline at end of file diff --git a/theme-compiler/tests/resources/automatic/scss/var-in-css-function.scss b/theme-compiler/tests/resources/automatic/scss/var-in-css-function.scss new file mode 100644 index 0000000000..72b371ba57 --- /dev/null +++ b/theme-compiler/tests/resources/automatic/scss/var-in-css-function.scss @@ -0,0 +1,15 @@ +$very_light: 98%; +$light: 90%; + +$hue1: 110; +$saturation1: 50%; +$very_light1: hsl($hue1, $saturation1, $very_light); +$light1: hsl($hue1, $saturation1, $light); + +$color: rgb(0, 255, 13); +.v-window-footer { + background: linear-gradient(bottom, $very_light1, $light1); + abc: rgba($color, .85); +} + + diff --git a/theme-compiler/tests/resources/automatic/scss/var-substring-match.scss b/theme-compiler/tests/resources/automatic/scss/var-substring-match.scss new file mode 100644 index 0000000000..5acfb799a1 --- /dev/null +++ b/theme-compiler/tests/resources/automatic/scss/var-substring-match.scss @@ -0,0 +1,8 @@ +@mixin mx($f: 10px) { + .foo { + font: { + size: $f; + } + } +} +@include mx; \ No newline at end of file diff --git a/theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java b/theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java new file mode 100644 index 0000000000..b05b0e9dcf --- /dev/null +++ b/theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java @@ -0,0 +1,53 @@ +package com.vaadin.sass.internal.util; + +import org.junit.Assert; +import org.junit.Test; + +public class StringUtilTest { + + @Test + public void testContainsVariable() { + String sentence = "$var1 var2"; + String word = "var"; + Assert.assertFalse(StringUtil.containsVariable(sentence, word)); + + word = "var1"; + Assert.assertTrue(StringUtil.containsVariable(sentence, word)); + + String var2 = "var2"; + Assert.assertFalse(StringUtil.containsVariable(sentence, var2)); + } + + @Test + public void testContainsVariableWithDash() { + String sentence = "$var- var2"; + String word = "var"; + Assert.assertFalse(StringUtil.containsVariable(sentence, word)); + } + + @Test + public void testReplaceVariable() { + String sentence = "$var1 var2"; + String word = "var"; + String value = "abc"; + Assert.assertEquals(sentence, + StringUtil.replaceVariable(sentence, word, value)); + + word = "var1"; + Assert.assertEquals("abc var2", + StringUtil.replaceVariable(sentence, word, value)); + + String var2 = "var2"; + Assert.assertEquals(sentence, + StringUtil.replaceVariable(sentence, var2, value)); + } + + @Test + public void testReplaceVariableWithDash() { + String sentence = "$var- var2"; + String word = "var"; + String value = "abc"; + Assert.assertEquals(sentence, + StringUtil.replaceVariable(sentence, word, value)); + } +} -- 2.39.5