Subversion Repositories general

Compare Revisions

Ignore whitespace Rev 956 → Rev 960

/backpath/trunk/src/ak/backpath/BackPath.java
0,0 → 1,394
package ak.backpath;
 
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;
import java.util.Set;
import java.util.TreeSet;
import java.util.Collections;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
import java.net.URL;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
 
public class BackPath
{
private static Log log = LogFactory.getLog(BackPath.class);
 
public static final String DEFAULT_KEY = BackPath.class.getName();
public static final String DEFAULT_PARAM = "backpath";
public static final String OUT_CHARSET = "UTF-8";
public static final String URL_CHARSET = "UTF-8";
public static final String IGNORES_DELIMITER = "\\s*;\\s*";
public static final boolean DEFAULT_ZIP = true;
public static final String[] METHODS_TO_SAVE_PARAMS = {"GET"};
public static final char URL_PARAM_START = '?';
public static final char URL_PARAM_SEPARATOR = '&';
public static final char URL_PARAM_VALUE = '=';
public static final char URL_PARAM_VALUE_SEP = ',';
 
protected static final byte URL_DELIMITER = 0;
protected static final int BUFFER_SIZE = 1024;
protected static final int INIT_STREAM_SIZE_COEF = 100;
protected static final String URL_PARAM_START_STR = "" + URL_PARAM_START;
 
protected static Set methodsToSaveParams;
 
// List(String)
protected List stack = new ArrayList();
protected String param = DEFAULT_PARAM;
protected String[] ignoreParams = null;
 
/** zip the stack */
protected boolean zip = DEFAULT_ZIP;
 
// string cache
protected String forwardParams;
protected boolean forwardParamsFormed = false;
protected String currentParams;
protected boolean currentParamsFormed = false;
protected String backwardParams;
protected boolean backwardParamsFormed = false;
protected String backwardUrl;
protected boolean backwardUrlFormed = false;
 
static {
methodsToSaveParams = Collections.unmodifiableSet(
new TreeSet(Arrays.asList(METHODS_TO_SAVE_PARAMS)));
}
 
protected static Deflater deflater = new Deflater();
protected static Inflater inflater = new Inflater();
 
public static BackPath findBackPath(HttpServletRequest request)
throws Exception
{
return findBackPath(request, DEFAULT_KEY, DEFAULT_PARAM, (String)null, DEFAULT_ZIP);
}
 
public static BackPath findBackPath(HttpServletRequest request, String backPathKey,
String backPathParam, String backPathIgnore, boolean zip)
throws Exception
{
Object backPathObj = request.getAttribute(backPathKey);
 
if(backPathObj == null) {
BackPath backPath;
String[] ignores = null;
 
if(backPathIgnore != null)
ignores = backPathIgnore.trim().split(IGNORES_DELIMITER);
 
backPath = new BackPath(request, backPathParam, ignores, zip);
request.setAttribute(backPathKey, backPath);
return backPath;
}
else if(backPathObj instanceof BackPath) {
return (BackPath)backPathObj;
}
else {
throw new Exception("An object under key " + backPathKey
+ " is not a " + BackPath.class.getName());
}
}
 
public BackPath(HttpServletRequest request, String param, String[] ignoreParams, boolean zip)
{
this.zip = zip;
this.param = param;
this.ignoreParams = ignoreParams;
init(request);
}
 
public boolean getZip()
{
return zip;
}
 
public void setZip(boolean zip)
{
this.zip = zip;
resetCachedStrings();
}
 
public String getParam()
{
return param;
}
 
public void setParam(String param)
{
this.param = param;
resetCachedStrings();
}
 
public String[] getIgnoreParams()
{
return ignoreParams;
}
 
public void setIgnoreParams(String[] ignoreParams)
{
this.ignoreParams = ignoreParams;
resetCachedStrings();
}
 
public boolean getHasBack()
{
return (stack.size() > 1);
}
 
// List(String)
public List getStack()
{
return Collections.unmodifiableList(stack);
}
 
public String getForwardParams()
{
if(!forwardParamsFormed) {
forwardParams = formForwardParams();
forwardParamsFormed = true;
}
 
return forwardParams;
}
 
protected String formForwardParams()
{
try {
return encode(stack, zip, 0);
}
catch(IOException ex) {
log.error("Cannot form forward params", ex);
return null;
}
}
 
public String getBackwardParams()
{
if(!backwardParamsFormed) {
backwardParams = formBackwardParams();
backwardParamsFormed = true;
}
 
return backwardParams;
}
 
protected String formBackwardParams()
{
try {
if(stack.size() <= 2)
return null;
else
return encode(stack, zip, 2);
}
catch(IOException ex) {
log.error("Cannot form backward params", ex);
return null;
}
}
 
public String getBackwardUrl()
{
if(!backwardParamsFormed) {
backwardUrl = formBackwardUrl();
backwardUrlFormed = true;
}
 
return backwardUrl;
}
 
protected String formBackwardUrl()
{
if(stack.size() < 2) return null;
 
try {
StringBuffer url = new StringBuffer((String)stack.get(stack.size()-2));
if(stack.size() > 2) {
String s = encode(stack, zip, 2);
if(url.indexOf(URL_PARAM_START_STR) >= 0) url.append(URL_PARAM_SEPARATOR);
else url.append(URL_PARAM_START);
url.append(param).append(URL_PARAM_VALUE).append(s);
}
 
return url.toString();
}
catch(IOException ex) {
log.error("Cannot form backward url", ex);
return null;
}
}
 
public String getCurrentParams()
{
if(!currentParamsFormed) {
currentParams = formCurrentParams();
currentParamsFormed = true;
}
 
return currentParams;
}
 
protected String formCurrentParams()
{
try {
if(stack.size() <= 1)
return null;
else
return encode(stack, zip, 1);
}
catch(IOException ex) {
log.error("Cannot form current params", ex);
return null;
}
}
 
public void init(HttpServletRequest request)
{
try {
resetCachedStrings();
// get old
decode(stack, request.getParameter(param), zip);
 
// add new
String url = (new URL(request.getRequestURL().toString())).getPath();
if(methodsToSaveParams.contains(request.getMethod())) {
// to ignore
Set ignore = new TreeSet();
if(ignoreParams != null) {
for(int i = 0; i < ignoreParams.length; i++)
ignore.add(ignoreParams[i]);
}
ignore.add(param);
 
// form query string
StringBuffer query = new StringBuffer();
Map requestParams = request.getParameterMap();
for(Iterator i = requestParams.keySet().iterator(); i.hasNext(); ) {
String name = (String)i.next();
if(ignore.contains(name)) continue;
if(query.length() == 0)
query.append(URL_PARAM_START);
else
query.append(URL_PARAM_SEPARATOR);
 
query.append(URLEncoder.encode(name, URL_CHARSET));
query.append(URL_PARAM_VALUE);
String[] values = (String[])requestParams.get(name);
for(int j = 0; j < values.length; j++) {
if(j > 0) query.append(URL_PARAM_VALUE_SEP);
query.append(URLEncoder.encode(values[j], URL_CHARSET));
}
}
 
// form url
if(query.length() > 0) url = query.insert(0, url).toString();
}
stack.add(url);
}
catch(IOException ex) {
log.error("Cannot init", ex);
}
}
 
/**
* @param stack List(String)
*/
protected String encode(List stack, boolean zip, int suffix)
throws IOException
{
ByteArrayOutputStream buf = new ByteArrayOutputStream(stack.size()*INIT_STREAM_SIZE_COEF);
Object lock = (zip ? (Object)BackPath.class : this);
 
synchronized(lock) {
OutputStream out;
if(zip) {
deflater.reset();
out = new DeflaterOutputStream(new Base64.OutputStream(buf), deflater);
}
else {
out = new Base64.OutputStream(buf);
}
 
for(int i = 0; i < stack.size()-suffix; i++) {
String s = (String)stack.get(i);
if(i > 0) out.write(URL_DELIMITER);
out.write(s.getBytes(URL_CHARSET));
}
out.close();
}
 
return buf.toString(OUT_CHARSET);
}
 
/**
* @param stack List(String)
*/
protected void decode(List stack, String path, boolean zip)
throws IOException
{
stack.clear();
if(path == null || path.equals("")) return;
 
ByteArrayOutputStream dec = new ByteArrayOutputStream(stack.size()*INIT_STREAM_SIZE_COEF);
Object lock = (zip ? (Object)BackPath.class : this);
 
synchronized(lock) {
ByteArrayInputStream enc = new ByteArrayInputStream(path.getBytes(OUT_CHARSET));
 
InputStream in;
if(zip) {
inflater.reset();
in = new InflaterInputStream(new Base64.InputStream(enc), inflater);
}
else {
in = new Base64.InputStream(enc);
}
 
byte[] tmp = new byte[BUFFER_SIZE];
int l;
while((l = in.read(tmp, 0, tmp.length)) > 0) {
dec.write(tmp, 0, l);
}
}
 
try {
byte[] buf = dec.toByteArray();
for(int start = 0, end = 0; end <= buf.length; end++) {
if(end == buf.length || buf[end] == URL_DELIMITER) {
stack.add(new String(buf, start, end-start, URL_CHARSET));
start = end+1;
}
}
}
catch(Exception ex) { // if some mistake in stack, then ignore it completely
stack.clear();
log.info("Cannot parse stack", ex);
}
}
 
protected void resetCachedStrings()
{
forwardParams = null;
forwardParamsFormed = false;
currentParams = null;
currentParamsFormed = false;
backwardParams = null;
backwardParamsFormed = false;
backwardUrl = null;
backwardUrlFormed = false;
}
}