@@ -59,68 +59,96 @@ class CssPropertyNormalizer {
5959 * @return the normalized property
6060 */
6161 public static String normalize (String str ) {
62- StringBuilder buffer = new StringBuilder ();
63- int segmentStart = 0 ;
64- for (int i = 0 ; i < str .length (); ++i ) {
62+ StringBuilder sb = new StringBuilder ();
63+ boolean isWhitespace = false ;
64+ int i = 0 ;
65+ while (i < str .length ()) {
6566 if (str .charAt (i ) == '\\' ) {
67+ sb .append (str .charAt (i ));
6668 ++i ;
67- } else if (str .charAt (i ) == '\'' || str .charAt (i ) == '"' ) {
68- appendAndFormatSegment (buffer , str , segmentStart , i + 1 );
69- segmentStart = i = appendQuoteContent (buffer , str , i + 1 , str .charAt (i ));
70- }
71- }
72- if (segmentStart < str .length ()) {
73- appendAndFormatSegment (buffer , str , segmentStart , str .length ());
74- }
75- return buffer .toString ();
76- }
77-
78- /**
79- * Appends and formats a segment.
80- *
81- * @param buffer the current buffer
82- * @param source a source
83- * @param start where to start in the source
84- * @param end where to end in the source
85- */
86- private static void appendAndFormatSegment (StringBuilder buffer , String source , int start , int end ) {
87- String [] parts = source .substring (start , end ).split ("\\ s" );
88- StringBuilder sb = new StringBuilder ();
89- for (String part : parts ) {
90- if (part .length () > 0 ) {
91- if (sb .length () > 0 && !trimSpaceAfter (sb .charAt (sb .length () - 1 )) && !trimSpaceBefore (part .charAt (0 ))) {
92- sb .append (" " );
69+ if (i < str .length ()) {
70+ sb .append (str .charAt (i ));
71+ ++i ;
72+ }
73+ } else if (Character .isWhitespace (str .charAt (i ))) {
74+ isWhitespace = true ;
75+ ++i ;
76+ } else {
77+ if (isWhitespace ) {
78+ if (sb .length () > 0 && !trimSpaceAfter (sb .charAt (sb .length () - 1 )) && !trimSpaceBefore (str .charAt (i ))) {
79+ sb .append (" " );
80+ }
81+ isWhitespace = false ;
9382 }
94- // Do not make base64 data lowercase, function name only
95- if (part .matches ("^[uU][rR][lL]\\ (.+\\ )" ) && CssUtils .isBase64Data (part .substring (4 , part .length () - 1 ))) {
96- sb .append (part .substring (0 , 3 ).toLowerCase ()).append (part .substring (3 ));
83+ if (str .charAt (i ) == '\'' || str .charAt (i ) == '"' ) {
84+ i = appendQuotedString (sb , str , i );
85+ } else if ((str .charAt (i ) == 'u' || str .charAt (i ) == 'U' ) && str .substring (i ).matches ("^[uU][rR][lL]\\ (.*?" )) {
86+ sb .append (str .substring (i , i + 4 ).toLowerCase ());
87+ i = appendUrlContent (sb , str , i + 4 );
9788 } else {
98- sb .append (part .toLowerCase ());
89+ sb .append (Character .toLowerCase (str .charAt (i )));
90+ ++i ;
9991 }
10092 }
10193 }
102- buffer . append ( sb );
94+ return sb . toString ( );
10395 }
10496
10597 /**
106- * Appends quoted content .
98+ * Appends quoted string .
10799 *
108100 * @param buffer the current buffer
109101 * @param source a source
110- * @param start where to start in the source
111- * @param endQuoteSymbol the end quote symbol
102+ * @param start where to start in the source. Should point at quote symbol.
112103 * @return the new position in the source
113104 */
114- private static int appendQuoteContent (StringBuilder buffer , String source , int start , char endQuoteSymbol ) {
115- int end = CssUtils .findNextUnescapedChar (source , endQuoteSymbol , start );
105+ private static int appendQuotedString (StringBuilder buffer , String source , int start ) {
106+ char endQuoteSymbol = source .charAt (start );
107+ int end = CssUtils .findNextUnescapedChar (source , endQuoteSymbol , start + 1 );
116108 if (end == -1 ) {
117109 end = source .length ();
118110 LoggerFactory .getLogger (CssPropertyNormalizer .class ).warn (MessageFormatUtil .format (LogMessageConstant .QUOTE_IS_NOT_CLOSED_IN_CSS_EXPRESSION , source ));
111+ } else {
112+ ++end ;
119113 }
120114 buffer .append (source , start , end );
121115 return end ;
122116 }
123117
118+ /**
119+ * Appends url content and end parenthesis if url is correct.
120+ *
121+ * @param buffer the current buffer
122+ * @param source a source
123+ * @param start where to start in the source. Should point at first symbol after "url(".
124+ * @return the new position in the source
125+ */
126+ private static int appendUrlContent (StringBuilder buffer , String source , int start ) {
127+ while (Character .isWhitespace (source .charAt (start )) && start < source .length ()) {
128+ ++start ;
129+ }
130+ if (start < source .length ()) {
131+ int curr = start ;
132+ if (source .charAt (curr ) == '"' || source .charAt (curr ) == '\'' ) {
133+ curr = appendQuotedString (buffer , source , curr );
134+ return curr ;
135+ } else {
136+ curr = CssUtils .findNextUnescapedChar (source , ')' , curr );
137+ if (curr == -1 ) {
138+ LoggerFactory .getLogger (CssPropertyNormalizer .class ).warn (MessageFormatUtil .format (LogMessageConstant .URL_IS_NOT_CLOSED_IN_CSS_EXPRESSION , source ));
139+ return source .length ();
140+ } else {
141+ buffer .append (source .substring (start , curr ).trim ());
142+ buffer .append (')' );
143+ return curr + 1 ;
144+ }
145+ }
146+ } else {
147+ LoggerFactory .getLogger (CssPropertyNormalizer .class ).warn (MessageFormatUtil .format (LogMessageConstant .URL_IS_EMPTY_IN_CSS_EXPRESSION , source ));
148+ return source .length ();
149+ }
150+ }
151+
124152 /**
125153 * Checks if spaces can be trimmed after a specific character.
126154 *
0 commit comments