@ -22,9 +22,10 @@ import java.nio.charset.Charset;
@@ -22,9 +22,10 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.HashSet ;
import java.util.List ;
import java.util.Set ;
import java.util.SortedSet ;
import java.util.TreeSet ;
import org.apache.commons.logging.Log ;
import org.apache.commons.logging.LogFactory ;
@ -83,27 +84,27 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
@@ -83,27 +84,27 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
logger . trace ( "Transforming resource: " + newResource ) ;
}
byte [ ] bytes = new byte [ 0 ] ;
byte [ ] bytes ;
try {
bytes = FileCopyUtils . copyToByteArray ( newResource . getInputStream ( ) ) ;
}
catch ( IOException ex ) {
return Mono . error ( Exceptions . propagate ( ex ) ) ;
}
String full Content = new String ( bytes , DEFAULT_CHARSET ) ;
List < Segment > segment s = parseContent ( full Content) ;
String css Content = new String ( bytes , DEFAULT_CHARSET ) ;
List < ContentChunkInfo > contentChunkInfo s = parseContent ( css Content) ;
if ( segment s. isEmpty ( ) ) {
if ( contentChunkInfo s. isEmpty ( ) ) {
if ( logger . isTraceEnabled ( ) ) {
logger . trace ( "No links found." ) ;
}
return Mono . just ( newResource ) ;
}
return Flux . fromIterable ( segment s)
. concatMap ( segment - > {
String segmentContent = segment . getContent ( full Content) ;
if ( segment . isLink ( ) & & ! hasScheme ( segmentContent ) ) {
return Flux . fromIterable ( contentChunkInfo s)
. concatMap ( contentChunkInfo - > {
String segmentContent = contentChunkInfo . getContent ( css Content) ;
if ( contentChunkInfo . isLink ( ) & & ! hasScheme ( segmentContent ) ) {
String link = toAbsolutePath ( segmentContent , exchange ) ;
return resolveUrlPath ( link , exchange , newResource , transformerChain )
. defaultIfEmpty ( segmentContent ) ;
@ -116,39 +117,30 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
@@ -116,39 +117,30 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
writer . write ( chunk ) ;
return writer ;
} )
. flatM ap( writer - > {
. m ap( writer - > {
byte [ ] newContent = writer . toString ( ) . getBytes ( DEFAULT_CHARSET ) ;
return Mono . just ( new TransformedResource ( resource , newContent ) ) ;
return new TransformedResource ( resource , newContent ) ;
} ) ;
} ) ;
}
private List < Segment > parseContent ( String fullContent ) {
List < Segment > links = new ArrayList < > ( ) ;
for ( LinkParser parser : this . linkParsers ) {
links . addAll ( parser . parseLinks ( fullContent ) ) ;
}
private List < ContentChunkInfo > parseContent ( String cssContent ) {
SortedSet < ContentChunkInfo > links = new TreeSet < > ( ) ;
this . linkParsers . forEach ( parser - > parser . parse ( cssContent , links ) ) ;
if ( links . isEmpty ( ) ) {
return Collections . emptyList ( ) ;
}
Collections . sort ( links ) ;
int index = 0 ;
List < Segment > allSegments = new ArrayList < > ( links ) ;
for ( Segment link : links ) {
allSegments . add ( new Segment ( index , link . getStart ( ) , false ) ) ;
List < ContentChunkInfo > result = new ArrayList < > ( ) ;
for ( ContentChunkInfo link : links ) {
result . add ( new ContentChunkInfo ( index , link . getStart ( ) , false ) ) ;
result . add ( link ) ;
index = link . getEnd ( ) ;
}
if ( index < full Content. length ( ) ) {
allSegments . add ( new Segment ( index , full Content. length ( ) , false ) ) ;
if ( index < css Content. length ( ) ) {
result . add ( new ContentChunkInfo ( index , css Content. length ( ) , false ) ) ;
}
Collections . sort ( allSegments ) ;
return allSegments ;
return result ;
}
private boolean hasScheme ( String link ) {
@ -157,66 +149,60 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
@@ -157,66 +149,60 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
}
/ * *
* Extract content chunks that represent links .
* /
@FunctionalInterface
protected interface LinkParser {
Set < Segment > parseLinks ( String fullConten t) ;
void parse ( String cssContent , SortedSet < ContentChunkInfo > resul t) ;
}
protected static abstract class AbstractLinkParser implements LinkParser {
/** Return the keyword to use to search for links. */
/** Return the keyword to use to search for links, e .g. "@import", "url(" */
protected abstract String getKeyword ( ) ;
@Override
public Set < Segment > parseLinks ( String fullContent ) {
Set < Segment > linksToAdd = new HashSet < > ( 8 ) ;
int index = 0 ;
do {
index = fullContent . indexOf ( getKeyword ( ) , index ) ;
if ( index = = - 1 ) {
break ;
public void parse ( String content , SortedSet < ContentChunkInfo > result ) {
int position = 0 ;
while ( true ) {
position = content . indexOf ( getKeyword ( ) , position ) ;
if ( position = = - 1 ) {
return ;
}
index = skipWhitespace ( fullContent , index + getKeyword ( ) . length ( ) ) ;
if ( fullContent . charAt ( index ) = = '\'' ) {
index = addLink ( index , "'" , fullContent , linksToAdd ) ;
position + = getKeyword ( ) . length ( ) ;
while ( Character . isWhitespace ( content . charAt ( position ) ) ) {
position + + ;
}
else if ( fullC ontent. charAt ( index ) = = '" ' ) {
index = addLink ( index , "\"" , fullContent , linksToAdd ) ;
if ( c ontent. charAt ( posit io n) = = '\' ' ) {
position = extractLink ( position , '\'' , content , result ) ;
}
else {
index = extractLink ( index , fullContent , linksToAdd ) ;
else if ( content . charAt ( position ) = = '"' ) {
position = extractLink ( position , '"' , content , result ) ;
}
}
while ( true ) ;
return linksToAdd ;
}
else {
position = extractUnquotedLink ( position , content , result ) ;
private int skipWhitespace ( String content , int index ) {
while ( true ) {
if ( Character . isWhitespace ( content . charAt ( index ) ) ) {
index + + ;
continue ;
}
return index ;
}
}
protected int add Link( int index , String endKey , String content , Set < Segment > linksToAdd ) {
protected int extractLink ( int index , char endChar , String content , Set < ContentChunkInfo > result ) {
int start = index + 1 ;
int end = content . indexOf ( endKey , start ) ;
linksToAdd . add ( new Segment ( start , end , true ) ) ;
return end + endKey . length ( ) ;
int end = content . indexOf ( endChar , start ) ;
result . add ( new ContentChunkInfo ( start , end , true ) ) ;
return end + 1 ;
}
/ * *
* Invoked after a keyword match , after whitespaces removed , and when
* the next char is neither a single nor double quote .
* /
protected abstract int extractLink ( int index , String content , Set < Segment > linksToAdd ) ;
protected abstract int extractUnquotedLink ( int position , String content ,
Set < ContentChunkInfo > linksToAdd ) ;
}
@ -229,14 +215,14 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
@@ -229,14 +215,14 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
}
@Override
protected int extractLink ( int index , String content , Set < Segment > linksToAdd ) {
if ( content . substring ( index , index + 4 ) . equals ( "url(" ) ) {
// Ignore, UrlLink Parser will take care
protected int extractUnquoted Link ( int posit io n, String content , Set < ContentChunkInfo > result ) {
if ( content . substring ( posit io n, posit io n + 4 ) . equals ( "url(" ) ) {
// Ignore, UrlFunctionContent Parser will take care
}
else if ( logger . isErrorEnabled ( ) ) {
logger . error ( "Unexpected syntax for @import link at index " + index ) ;
logger . error ( "Unexpected syntax for @import link at index " + posit io n) ;
}
return index ;
return posit io n;
}
}
@ -249,26 +235,26 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
@@ -249,26 +235,26 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
}
@Override
protected int extractLink ( int index , String content , Set < Segment > linksToAdd ) {
protected int extractUnquoted Link ( int posit io n, String content , Set < ContentChunkInfo > result ) {
// A url() function without unquoted
return add Link( index - 1 , ")" , content , linksToAdd ) ;
return extract Link( posit io n - 1 , ')' , content , result ) ;
}
}
private static class Segment implements Comparable < Segment > {
private static class ContentChunkInfo implements Comparable < ContentChunkInfo > {
private final int start ;
private final int end ;
private final boolean l ink;
private final boolean isL ink;
public Segment ( int start , int end , boolean isLink ) {
ContentChunkInfo ( int start , int end , boolean isLink ) {
this . start = start ;
this . end = end ;
this . l ink = isLink ;
this . isL ink = isLink ;
}
@ -281,7 +267,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
@@ -281,7 +267,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
}
public boolean isLink ( ) {
return this . l ink;
return this . isL ink;
}
public String getContent ( String fullContent ) {
@ -289,7 +275,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
@@ -289,7 +275,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
}
@Override
public int compareTo ( Segment other ) {
public int compareTo ( ContentChunkInfo other ) {
return ( this . start < other . start ? - 1 : ( this . start = = other . start ? 0 : 1 ) ) ;
}
@ -298,8 +284,8 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
@@ -298,8 +284,8 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
if ( this = = obj ) {
return true ;
}
if ( obj ! = null & & obj instanceof Segment ) {
Segment other = ( Segment ) obj ;
if ( obj ! = null & & obj instanceof ContentChunkInfo ) {
ContentChunkInfo other = ( ContentChunkInfo ) obj ;
return ( this . start = = other . start & & this . end = = other . end ) ;
}
return false ;