Browse Source

Polish CssLinkResourceTransformer

pull/1480/head
Rossen Stoyanchev 9 years ago
parent
commit
b0fc83ee05
  1. 91
      spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java

91
spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java

@ -22,9 +22,10 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -82,25 +83,23 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream()); byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
String content = new String(bytes, DEFAULT_CHARSET); String content = new String(bytes, DEFAULT_CHARSET);
List<Segment> linkSegments = new ArrayList<>(8); SortedSet<ContentChunkInfo> links = new TreeSet<>();
for (LinkParser parser : this.linkParsers) { for (LinkParser parser : this.linkParsers) {
linkSegments.addAll(parser.parseLink(content)); parser.parse(content, links);
} }
if (linkSegments.isEmpty()) { if (links.isEmpty()) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("No links found."); logger.trace("No links found.");
} }
return resource; return resource;
} }
Collections.sort(linkSegments);
int index = 0; int index = 0;
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
for (Segment linkSegment : linkSegments) { for (ContentChunkInfo linkContentChunkInfo : links) {
writer.write(content.substring(index, linkSegment.getStart())); writer.write(content.substring(index, linkContentChunkInfo.getStart()));
String link = content.substring(linkSegment.getStart(), linkSegment.getEnd()); String link = content.substring(linkContentChunkInfo.getStart(), linkContentChunkInfo.getEnd());
String newLink = null; String newLink = null;
if (!hasScheme(link)) { if (!hasScheme(link)) {
String absolutePath = toAbsolutePath(link, request); String absolutePath = toAbsolutePath(link, request);
@ -115,7 +114,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
} }
writer.write(newLink != null ? newLink : link); writer.write(newLink != null ? newLink : link);
index = linkSegment.getEnd(); index = linkContentChunkInfo.getEnd();
} }
writer.write(content.substring(index)); writer.write(content.substring(index));
@ -128,60 +127,51 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
/**
* Extract content chunks that represent links.
*/
@FunctionalInterface @FunctionalInterface
protected interface LinkParser { protected interface LinkParser {
Set<Segment> parseLink(String content); void parse(String content, SortedSet<ContentChunkInfo> result);
} }
protected static abstract class AbstractLinkParser implements LinkParser { protected static abstract class AbstractLinkParser implements LinkParser {
/** /** Return the keyword to use to search for links, e.g. "@import", "url(" */
* Return the keyword to use to search for links.
*/
protected abstract String getKeyword(); protected abstract String getKeyword();
@Override @Override
public Set<Segment> parseLink(String content) { public void parse(String content, SortedSet<ContentChunkInfo> result) {
Set<Segment> linksToAdd = new HashSet<>(8); int position = 0;
int index = 0; while (true) {
do { position = content.indexOf(getKeyword(), position);
index = content.indexOf(getKeyword(), index); if (position == -1) {
if (index == -1) { return;
break;
} }
index = skipWhitespace(content, index + getKeyword().length()); position += getKeyword().length();
if (content.charAt(index) == '\'') { while (Character.isWhitespace(content.charAt(position))) {
index = addLink(index, "'", content, linksToAdd); position++;
} }
else if (content.charAt(index) == '"') { if (content.charAt(position) == '\'') {
index = addLink(index, "\"", content, linksToAdd); position = extractLink(position, "'", content, result);
} }
else { else if (content.charAt(position) == '"') {
index = extractLink(index, content, linksToAdd); position = extractLink(position, "\"", content, result);
} }
} else {
while (true); position = extractLink(position, content, result);
return linksToAdd;
}
private int skipWhitespace(String content, int index) {
while (true) {
if (Character.isWhitespace(content.charAt(index))) {
index++;
continue;
} }
return index;
} }
} }
protected int addLink(int index, String endKey, String content, Set<Segment> linksToAdd) { protected int extractLink(int index, String endKey, String content, SortedSet<ContentChunkInfo> linksToAdd) {
int start = index + 1; int start = index + 1;
int end = content.indexOf(endKey, start); int end = content.indexOf(endKey, start);
linksToAdd.add(new Segment(start, end)); linksToAdd.add(new ContentChunkInfo(start, end));
return end + endKey.length(); return end + endKey.length();
} }
@ -189,7 +179,8 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
* Invoked after a keyword match, after whitespaces removed, and when * Invoked after a keyword match, after whitespaces removed, and when
* the next char is neither a single nor double quote. * the next char is neither a single nor double quote.
*/ */
protected abstract int extractLink(int index, String content, Set<Segment> linksToAdd); protected abstract int extractLink(int index, String content,
SortedSet<ContentChunkInfo> linksToAdd);
} }
@ -202,7 +193,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
@Override @Override
protected int extractLink(int index, String content, Set<Segment> linksToAdd) { protected int extractLink(int index, String content, SortedSet<ContentChunkInfo> linksToAdd) {
if (content.substring(index, index + 4).equals("url(")) { if (content.substring(index, index + 4).equals("url(")) {
// Ignore, UrlLinkParser will take care // Ignore, UrlLinkParser will take care
} }
@ -222,20 +213,20 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
@Override @Override
protected int extractLink(int index, String content, Set<Segment> linksToAdd) { protected int extractLink(int index, String content, SortedSet<ContentChunkInfo> linksToAdd) {
// A url() function without unquoted // A url() function without unquoted
return addLink(index - 1, ")", content, linksToAdd); return extractLink(index - 1, ")", content, linksToAdd);
} }
} }
private static class Segment implements Comparable<Segment> { private static class ContentChunkInfo implements Comparable<ContentChunkInfo> {
private final int start; private final int start;
private final int end; private final int end;
public Segment(int start, int end) { ContentChunkInfo(int start, int end) {
this.start = start; this.start = start;
this.end = end; this.end = end;
} }
@ -249,7 +240,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
@Override @Override
public int compareTo(Segment other) { public int compareTo(ContentChunkInfo other) {
return (this.start < other.start ? -1 : (this.start == other.start ? 0 : 1)); return (this.start < other.start ? -1 : (this.start == other.start ? 0 : 1));
} }
@ -258,8 +249,8 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
if (this == obj) { if (this == obj) {
return true; return true;
} }
if (obj instanceof Segment) { if (obj instanceof ContentChunkInfo) {
Segment other = (Segment) obj; ContentChunkInfo other = (ContentChunkInfo) obj;
return (this.start == other.start && this.end == other.end); return (this.start == other.start && this.end == other.end);
} }
return false; return false;

Loading…
Cancel
Save