/*
* This file is part of the Wayback archival access software
* (http://archive-access.sourceforge.net/projects/wayback/).
*
* Licensed to the Internet Archive (IA) by one or more individual
* contributors.
*
* The IA licenses this file to You 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 org.archive.wayback.util.graph;
/**
* @author brad
*
*/
public class GraphEncoder {
private static String DELIM = "_";
private static String REGION_DELIM = ":";
/**
* convert a String-encoded graph into a usable Graph object, using
* default GraphConfiguration
* @param encodedGraph String encoded graph, as returned by getEncoded()
* @param noMonth if true, disable the month highlight color
* @return a Graph, ready to use
* @throws GraphEncodingException if there were problems with the encoded
* data
*/
public static Graph decode(String encodedGraph, boolean noMonth)
throws GraphEncodingException {
GraphConfiguration config = new GraphConfiguration();
if(noMonth) {
config.valueHighlightColor = config.valueColor;
}
return decode(encodedGraph, config);
}
/**
* convert a String-encoded graph into a usable Graph object, using
* the provided GraphConfiguration.
* @param encodedGraph String encoded graph, as returned by getEncoded()
* @param config the GraphConfiguration to use
* @return a Graph, ready to use
* @throws GraphEncodingException if there were problems with the encoded
* data
*/
public static Graph decode(String encodedGraph, GraphConfiguration config)
throws GraphEncodingException {
// encoded = "800_35_REGIONDATA_REGIONDATA_REGIONDATA_REGIONDATA_..."
String parts[] = encodedGraph.split(DELIM);
int numRegions = parts.length - 2;
if(parts.length < 1) {
throw new GraphEncodingException("No regions defined!");
}
int width;
int height;
try {
width = Integer.parseInt(parts[0]);
} catch(NumberFormatException e) {
throw new GraphEncodingException("Bad integer width:" + parts[0]);
}
try {
height = Integer.parseInt(parts[1]);
} catch(NumberFormatException e) {
throw new GraphEncodingException("Bad integer width:" + parts[0]);
}
RegionData data[] = new RegionData[numRegions];
for(int i = 0; i < numRegions; i++) {
// REGIONDATA = "2001:-1:0ab3f70023f902f"
// LABEL:ACTIVE_IDX:HEXDATA
String regionParts[] = parts[i + 2].split(REGION_DELIM);
if(regionParts.length != 3) {
throw new GraphEncodingException("Wrong number of parts in " +
parts[i+2]);
}
int highlightedValue = Integer.parseInt(regionParts[1]);
int values[] = decodeHex(regionParts[2]);
data[i] = new RegionData(regionParts[0], highlightedValue, values);
}
return new Graph(width, height, data, config);
}
/**
* Convert a complete Graph into an opaque String that can later be
* re-assembled into a Graph object. Note that GraphConfiguration
* information is NOT encoded into the opaque String.
* @param g Graph to encode
* @return opaque String which can later be used with decode()
*/
public static String encode(Graph g) {
RegionGraphElement rge[] = g.getRegions();
RegionData data[] = new RegionData[rge.length];
for(int i = 0; i < data.length; i++) {
data[i] = rge[i].getData();
}
return encode(g.width, g.height, data);
}
/**
* Convert a Graph fields into an opaque String that can later be
* re-assembled into a Graph object. Note that GraphConfiguration
* information is NOT encoded into the opaque String.
* @param width of the Graph
* @param height of the Graph
* @param data array of RegionData for the graph
* @return opaque String which can later be used with decode()
*/
public static String encode(int width, int height, RegionData data[]) {
StringBuilder sb = new StringBuilder();
sb.append(width).append(DELIM);
sb.append(height);
boolean first = false;
for(RegionData datum : data) {
if(first) {
first = false;
} else {
sb.append(DELIM);
}
sb.append(datum.getLabel()).append(REGION_DELIM);
sb.append(datum.getHighlightedValue()).append(REGION_DELIM);
sb.append(encodeHex(datum.getValues()));
}
return sb.toString();
}
public static String encodeHex(int values[]) {
StringBuilder sb = new StringBuilder(values.length);
for(int value : values) {
if((value > 15) || (value < 0)){
throw new IllegalArgumentException();
}
sb.append(Integer.toHexString(value));
}
return sb.toString();
}
private static int[] decodeHex(String hexString) {
int length = hexString.length();
int values[] = new int[length];
for(int i = 0; i < length; i++) {
char c = hexString.charAt(i);
if(c >= '0') {
if(c <= '9') {
values[i] = c - '0';
} else {
if(c > 'f') {
throw new IllegalArgumentException();
} else {
if(c >= 'a') {
values[i] = c - 'W';
} else {
throw new IllegalArgumentException();
}
}
}
} else {
throw new IllegalArgumentException();
}
}
return values;
}
}