/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.jstestdriver.token;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Map;
/**
* Automatically escapes \, ', ", \n, \f, \r when writing to the underlying writer.
* (Why isn't the Gson Escaper public? Arg!)
* Not Thread Safe!
* @author corbinrsmith@gmail.com (Cory Smith)
*
*/
public class EscapingWriter extends FilterWriter {
private static final Map<Character, char[]> REPLACE = ImmutableMap
.<Character, char[]>builder()
.put('\\', new char[] {'\\', '\\'})
.put('\n', new char[] {'\\', 'n'})
.put('\r', new char[] {'\\', 'r'})
.put('\f', new char[] {'\\', 'f'})
.put('\'', new char[] {'\\', '\''})
.put('"', new char[] {'\\', '"'})
.build();
private final char[] escapedBuffer = new char[256];
private Map<Character, Integer> escapedCounts = Maps.newHashMap();
public EscapingWriter(Writer out) {
super(out);
for (Character key : REPLACE.keySet()) {
escapedCounts.put(key, 0);
}
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
int pos = 0;
for (int i = off; i < len; i++ ) {
Character character = Character.valueOf(cbuf[i]);
if (REPLACE.containsKey(character)) {
pos = writeToBuffer(pos, REPLACE.get(character));
// TODO(corysmith): find a better method to accomplish this in a performant manner.
escapedCounts.put(character, escapedCounts.get(character) + 1);
} else {
pos = writeToBuffer(pos, character);
}
}
out.write(escapedBuffer, 0, pos);
}
private int writeToBuffer(int pos, char c) throws IOException {
if (pos + 1 > escapedBuffer.length) { // flush it
out.write(escapedBuffer, 0, pos);
pos = 0;
}
escapedBuffer[pos++] = c;
return pos;
}
private int writeToBuffer(int pos, char[] characters) throws IOException {
if (pos + characters.length > escapedBuffer.length) { // flush it
out.write(escapedBuffer, 0, pos);
pos = 0;
}
System.arraycopy(characters, 0, escapedBuffer, 0, pos);
pos += characters.length;
return pos;
}
@Override
public void write(int c) throws IOException {
Character chr = Character.valueOf((char)c);
if (REPLACE.containsKey(chr)) {
out.write(REPLACE.get(chr));
// TODO(corysmith): find a better method to accomplish this in a performant manner.
escapedCounts.put(chr, escapedCounts.get(chr) + 1);
} else {
out.write(c);
}
}
@Override
public void write(char[] cbuf) throws IOException {
write(cbuf, 0, cbuf.length);
}
public int getEscapedCount(char character) {
return escapedCounts.get(character);
}
@Override
public String toString() {
return "EscapingWriter [escapedBuffer=" + Arrays.toString(escapedBuffer) + ", escapedCounts="
+ escapedCounts + "]";
}
}