Skip to content

Instantly share code, notes, and snippets.

@lfryc
Created September 29, 2011 20:53
Show Gist options
  • Save lfryc/1251921 to your computer and use it in GitHub Desktop.
Save lfryc/1251921 to your computer and use it in GitHub Desktop.
package org.richfaces.demo;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jboss.weld.exceptions.UnsupportedOperationException;
public class MultipleScriptTagPrintWriter extends PrintWriter {
private final static String HEAD_START = "<head";
private final static String HEAD_STOP = "</head";
private final static Pattern SCRIPT_PATTERN1 = Pattern.compile("<script ([^>]*)/>");
private final static Pattern SCRIPT_PATTERN2 = Pattern.compile("<script ([^>]*)></script>");
private final static Pattern SRC_PATTERN = Pattern.compile("src=\"([^\"]+)\"");
private TokenMatcher headStart = new TokenMatcher(HEAD_START);
private TokenMatcher headStop = new TokenMatcher(HEAD_STOP);
private TokenPairMatcher headPair = new TokenPairMatcher(headStart, headStop);
private boolean headProcessed = false;
private StringBuffer cache = new StringBuffer();
public MultipleScriptTagPrintWriter(Writer out, boolean autoFlush) {
super(out, autoFlush);
}
@Override
public void write(int b) {
try {
processHead(b);
} catch (IOException e) {
throw new IllegalStateException("Unexpected exception when processing " + this.getClass().getName(), e);
}
}
@Override
public void write(char[] buf) {
for (char c : buf) {
this.write(c);
}
}
@Override
public void write(char[] buf, int off, int len) {
for (int i = 0; i < len; i++) {
char c = buf[off + i];
this.write(c);
}
}
@Override
public void write(String s) {
for (char c : s.toCharArray()) {
this.write(c);
}
}
@Override
public void write(String s, int off, int len) {
this.write(s.toCharArray(), off, len);
}
private void processHead(int b) throws IOException {
if (headStop.matches()) {
if (!headProcessed) {
processScripts();
headProcessed = true;
}
out.write(b);
} else {
boolean matched = headPair.write(b);
if (headStart.matches()) {
cache.append((char) b);
} else {
if (matched) {
cache.append((char) b);
} else {
flushCacheToOut();
out.write(b);
}
}
}
}
private void processScripts() throws IOException {
int firstScriptPosition = getFirstScriptPosition();
if (firstScriptPosition >= 0) {
CharSequence scriptsSubstitution = getScriptsSubstitution();
cache = new StringBuffer(SCRIPT_PATTERN1.matcher(cache).replaceAll(""));
cache = new StringBuffer(SCRIPT_PATTERN2.matcher(cache).replaceAll(""));
cache.insert(firstScriptPosition, scriptsSubstitution);
}
flushCacheToOut();
}
private CharSequence getScriptsSubstitution() {
StringBuffer newScripts = new StringBuffer();
Set<String> sources = lookForScriptsSources();
for (String source : sources) {
newScripts.append("<script type=\"text/javascript\" src=\"");
newScripts.append(source);
newScripts.append("\"></script>");
}
return newScripts;
}
private int getFirstScriptPosition() {
int result = -1;
Matcher matcherScript = SCRIPT_PATTERN1.matcher(cache);
if (matcherScript.find()) {
result = matcherScript.start();
}
matcherScript = SCRIPT_PATTERN2.matcher(cache);
if (matcherScript.find()) {
if (result >= 0) {
result = Math.min(result, matcherScript.start());
} else {
result = matcherScript.start();
}
}
return result;
}
private Set<String> lookForScriptsSources() {
Set<String> scripts = new LinkedHashSet<String>();
Matcher matcherScript = SCRIPT_PATTERN1.matcher(cache);
while (matcherScript.find()) {
String script = matcherScript.group(1);
Matcher matcherSrc = SRC_PATTERN.matcher(script);
if (matcherSrc.find()) {
String src = matcherSrc.group(1);
scripts.add(src);
}
}
matcherScript = SCRIPT_PATTERN2.matcher(cache);
while (matcherScript.find()) {
String script = matcherScript.group(1);
Matcher matcherSrc = SRC_PATTERN.matcher(script);
if (matcherSrc.find()) {
String src = matcherSrc.group(1);
scripts.add(src);
}
}
return scripts;
}
private void flushCacheToOut() throws IOException {
if (cache.length() > 0) {
for (char c : cache.toString().toCharArray()) {
out.write(c);
}
resetCache();
}
}
private void resetCache() {
cache = new StringBuffer();
}
private class TokenPairMatcher {
TokenMatcher opening;
TokenMatcher closing;
public TokenPairMatcher(TokenMatcher open, TokenMatcher close) {
this.opening = open;
this.closing = close;
}
private boolean write(int b) {
boolean openingWritten = false;
if (!opening.matches()) {
openingWritten = opening.write(b);
} else {
if (!closing.matches()) {
closing.write(b);
}
}
return openingWritten;
}
private boolean matches() {
return opening.matches() && closing.matches();
}
private void reset() {
opening.reset();
closing.reset();
}
}
private class TokenMatcher {
private String pattern;
private StringBuffer buffer;
private boolean match;
public TokenMatcher(String pattern) {
this.pattern = pattern;
this.buffer = new StringBuffer(pattern.length());
}
private boolean write(int b) {
if (b == pattern.codePointAt(buffer.length())) {
char c = (char) b;
buffer.append(c);
if (buffer.length() == pattern.length()) {
match = true;
}
return true;
} else {
this.reset();
return false;
}
}
private boolean matches() {
return match;
}
private void reset() {
buffer = new StringBuffer();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment