Browse Source

Polish

pull/1476/head
Rossen Stoyanchev 9 years ago
parent
commit
d2c6ea5b1b
  1. 2
      spring-webflux/src/main/java/org/springframework/web/reactive/resource/AppCacheManifestTransformer.java
  2. 140
      spring-webflux/src/main/java/org/springframework/web/reactive/resource/CssLinkResourceTransformer.java

2
spring-webflux/src/main/java/org/springframework/web/reactive/resource/AppCacheManifestTransformer.java

@ -125,7 +125,7 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
return Flux.generate(new LineGenerator(content)) return Flux.generate(new LineGenerator(content))
.concatMap(info -> processLine(info, exchange, resource, chain)) .concatMap(info -> processLine(info, exchange, resource, chain))
.collect(() -> new LineAggregator(resource, content), LineAggregator::add) .collect(() -> new LineAggregator(resource, content), LineAggregator::add)
.flatMap(aggregator -> Mono.just(aggregator.createResource())); .map(LineAggregator::createResource);
}); });
} }

140
spring-webflux/src/main/java/org/springframework/web/reactive/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 org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -83,27 +84,27 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
logger.trace("Transforming resource: " + newResource); logger.trace("Transforming resource: " + newResource);
} }
byte[] bytes = new byte[0]; byte[] bytes;
try { try {
bytes = FileCopyUtils.copyToByteArray(newResource.getInputStream()); bytes = FileCopyUtils.copyToByteArray(newResource.getInputStream());
} }
catch (IOException ex) { catch (IOException ex) {
return Mono.error(Exceptions.propagate(ex)); return Mono.error(Exceptions.propagate(ex));
} }
String fullContent = new String(bytes, DEFAULT_CHARSET); String cssContent = new String(bytes, DEFAULT_CHARSET);
List<Segment> segments = parseContent(fullContent); List<ContentChunkInfo> contentChunkInfos = parseContent(cssContent);
if (segments.isEmpty()) { if (contentChunkInfos.isEmpty()) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("No links found."); logger.trace("No links found.");
} }
return Mono.just(newResource); return Mono.just(newResource);
} }
return Flux.fromIterable(segments) return Flux.fromIterable(contentChunkInfos)
.concatMap(segment -> { .concatMap(contentChunkInfo -> {
String segmentContent = segment.getContent(fullContent); String segmentContent = contentChunkInfo.getContent(cssContent);
if (segment.isLink() && !hasScheme(segmentContent)) { if (contentChunkInfo.isLink() && !hasScheme(segmentContent)) {
String link = toAbsolutePath(segmentContent, exchange); String link = toAbsolutePath(segmentContent, exchange);
return resolveUrlPath(link, exchange, newResource, transformerChain) return resolveUrlPath(link, exchange, newResource, transformerChain)
.defaultIfEmpty(segmentContent); .defaultIfEmpty(segmentContent);
@ -116,39 +117,30 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
writer.write(chunk); writer.write(chunk);
return writer; return writer;
}) })
.flatMap(writer -> { .map(writer -> {
byte[] newContent = writer.toString().getBytes(DEFAULT_CHARSET); 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) { private List<ContentChunkInfo> parseContent(String cssContent) {
SortedSet<ContentChunkInfo> links = new TreeSet<>();
List<Segment> links = new ArrayList<>(); this.linkParsers.forEach(parser -> parser.parse(cssContent, links));
for (LinkParser parser : this.linkParsers) {
links.addAll(parser.parseLinks(fullContent));
}
if (links.isEmpty()) { if (links.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
Collections.sort(links);
int index = 0; int index = 0;
List<Segment> allSegments = new ArrayList<>(links); List<ContentChunkInfo> result = new ArrayList<>();
for (Segment link : links) { for (ContentChunkInfo link : links) {
allSegments.add(new Segment(index, link.getStart(), false)); result.add(new ContentChunkInfo(index, link.getStart(), false));
result.add(link);
index = link.getEnd(); index = link.getEnd();
} }
if (index < fullContent.length()) { if (index < cssContent.length()) {
allSegments.add(new Segment(index, fullContent.length(), false)); result.add(new ContentChunkInfo(index, cssContent.length(), false));
} }
return result;
Collections.sort(allSegments);
return allSegments;
} }
private boolean hasScheme(String link) { private boolean hasScheme(String link) {
@ -157,66 +149,60 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
/**
* Extract content chunks that represent links.
*/
@FunctionalInterface @FunctionalInterface
protected interface LinkParser { protected interface LinkParser {
Set<Segment> parseLinks(String fullContent); void parse(String cssContent, 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. */ /** Return the keyword to use to search for links, e.g. "@import", "url(" */
protected abstract String getKeyword(); protected abstract String getKeyword();
@Override @Override
public Set<Segment> parseLinks(String fullContent) { 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 = fullContent.indexOf(getKeyword(), index); if (position == -1) {
if (index == -1) { return;
break;
} }
index = skipWhitespace(fullContent, index + getKeyword().length()); position += getKeyword().length();
if (fullContent.charAt(index) == '\'') { while (Character.isWhitespace(content.charAt(position))) {
index = addLink(index, "'", fullContent, linksToAdd); position++;
} }
else if (fullContent.charAt(index) == '"') { if (content.charAt(position) == '\'') {
index = addLink(index, "\"", fullContent, linksToAdd); position = extractLink(position, '\'', content, result);
} }
else { else if (content.charAt(position) == '"') {
index = extractLink(index, fullContent, linksToAdd); position = extractLink(position, '"', content, result);
} }
} else {
while (true); position = extractUnquotedLink(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, char endChar, String content, Set<ContentChunkInfo> result) {
int start = index + 1; int start = index + 1;
int end = content.indexOf(endKey, start); int end = content.indexOf(endChar, start);
linksToAdd.add(new Segment(start, end, true)); result.add(new ContentChunkInfo(start, end, true));
return end + endKey.length(); return end + 1;
} }
/** /**
* 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 extractUnquotedLink(int position, String content,
Set<ContentChunkInfo> linksToAdd);
} }
@ -229,14 +215,14 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
@Override @Override
protected int extractLink(int index, String content, Set<Segment> linksToAdd) { protected int extractUnquotedLink(int position, String content, Set<ContentChunkInfo> result) {
if (content.substring(index, index + 4).equals("url(")) { if (content.substring(position, position + 4).equals("url(")) {
// Ignore, UrlLinkParser will take care // Ignore, UrlFunctionContentParser will take care
} }
else if (logger.isErrorEnabled()) { else if (logger.isErrorEnabled()) {
logger.error("Unexpected syntax for @import link at index " + index); logger.error("Unexpected syntax for @import link at index " + position);
} }
return index; return position;
} }
} }
@ -249,26 +235,26 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
@Override @Override
protected int extractLink(int index, String content, Set<Segment> linksToAdd) { protected int extractUnquotedLink(int position, String content, Set<ContentChunkInfo> result) {
// A url() function without unquoted // A url() function without unquoted
return addLink(index - 1, ")", content, linksToAdd); return extractLink(position - 1, ')', content, result);
} }
} }
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;
private final boolean link; private final boolean isLink;
public Segment(int start, int end, boolean isLink) { ContentChunkInfo(int start, int end, boolean isLink) {
this.start = start; this.start = start;
this.end = end; this.end = end;
this.link = isLink; this.isLink = isLink;
} }
@ -281,7 +267,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
} }
public boolean isLink() { public boolean isLink() {
return this.link; return this.isLink;
} }
public String getContent(String fullContent) { public String getContent(String fullContent) {
@ -289,7 +275,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));
} }
@ -298,8 +284,8 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
if (this == obj) { if (this == obj) {
return true; return true;
} }
if (obj != null && obj instanceof Segment) { if (obj != null && 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