//this handler stores the camera positions
 
class CamHandler implements ExternalTokenHandler{
 
CameraState [] savedStates = new CameraState[9];
PApplet parent;
public CamHandler(PApplet parent){
this.parent = parent;
//clear basic states
for (int i =0; i < 9; i++){
savedStates[i] = new CameraState();
}
}
 
public void parseFile(VectorGraphic vg, String header, StringTokenizer st){
if (header.equals("cs")){
for (int i =0; i < 9; i++){
savedStates[i] = new CameraState(st.nextToken());
}
}
 
}
//this class doesn't do any rendering.
public void renderGraphic(VectorGraphic vg, String header, Vector cStroke){
}
public String tokenize(){
String toRet = "cs,";
for (int i =0; i < 9; i ++){
toRet += savedStates[i].toString() + ",";
}
return toRet;
}
 
 
}
public class ColorBox extends GUIComponent{
 
int fillColor = 0;
 
public void draw(){
 
g.fill(fillColor);
g.noStroke();
g.rect(locX+1,locY+1,cWidth-1,cHeight);
}
public void setFillColor(int fill){
fillColor = fill;
}
}
 
 
//this handler stores the camera positions
 
class LinkHandler implements ExternalTokenHandler{
 
 
PApplet parent;
Vector links;
public LinkHandler(PApplet parent){
this.parent = parent;
links = new Vector();
 
}
 
public void parseFile(VectorGraphic vg, String header, StringTokenizer st){
if (header.equals("ln")){
TokenPath tp = new TokenPath(st.nextToken());
CameraState cs = new CameraState(st.nextToken());
Vector toAdd = new Vector(3);
toAdd.add("ln");
toAdd.add(tp);
toAdd.add(cs);
vg.vector.add(toAdd);
}
 
}
public void renderGraphic(VectorGraphic vg, String header, Vector cStroke){
if (header.equals("ln")){
Graphics2D g2d;
g2d = ((PGraphicsJava2D)parent.g).g2;
g2d.setColor(new Color(0,0,0));
TokenPath tp = (TokenPath)cStroke.elementAt(1);
Tuple2f ml = cam.screenToWorld(mouseX,mouseY);
if (tp.contains(new Point2D.Float(ml.x,ml.y))){
g2d.setColor(new Color(0,0,255));
if (picking) cam.setState((CameraState)cStroke.elementAt(2));
}
g2d.draw(tp);
}
}
}
 
 
class Link{
Tuple2f center;
Tuple2f size;
CameraState state;
 
}
//created=24 Dec 07
//description=An infinite chalkboard
//title=Scrawble
//publish=false
//titlebar=An infinite chalkboard
//tags=drawing
 
import SGGUI.*;
import InterpolatedVar.*;
import SGPlanarCam.*;
import SGShape.*;
import SGImplify.*;
import Tuple.*;
import SGVectorGraphic.*;
 
import java.awt.geom.Path2D;
import java.awt.geom.*;
import javax.swing.*; //fix this later
 
 
SGSlider strokeThick;
SGSlider tolerance;
SGSlider worldSize;
SGSlider r,g,b,fr,fg,fb,fa;
 
SGLabel zoom;
 
SGButton openFile, saveFile, draw,fillDraw, enterText,zoomButton,move,undo, snapLink, makeLink,newFile,rot,settingsButton;
ColorBox strok, fil;
 
Vector txEnter;
 
SGGUI controller;
VerticalLayout settings;
VerticalLayout controls;
 
VectorGraphic vg;
 
 
boolean lp;
 
Vector currentStroke = null;
 
//for passing the
Pen pen = new Pen();
 
SGPlanarCam cam;
 
int modKey;
PFont fnt;
 
float dof = 50;
 
float currentScale = 1;
int pts = 0;
 
 
static final int DRAW_LINE = 0;
static final int FILL_LINE = 4;
static final int DRAW_LINK = 1;
static final int TEXT_ENTRY = 2;
static final int ZOOM = 5;
static final int TRANSLATE = 6;
static final int ROTATE = 7;
static final int NONE = 3;
 
int mode = TRANSLATE;
 
StringBuffer txt;
 
 
TextHandler txtHandle;
CamHandler camHandle;
LinkHandler linkHandle;
CameraState clipBoard = null;
 
 
boolean picking = false;
 
int penColor = 0;
int fillColor = 0;
 
 
String scriptQ = null;
 
CameraState baseCam;
 
void setup() {
size(550, 550);
smooth();
frameRate(30);
 
fnt = loadFont("writing.vlw");
textFont(fnt);
controller = new SGGUI(this,fnt);
controller.GUI_HIGHLIGHT = color(220,220,240);
settings = new VerticalLayout(controller,200,10,150);
settings.title = "Settings";
controls = new VerticalLayout(controller,10,10,32);
 
 
tolerance = new SGSlider( "Tolerance",3,.2,.1,10);
worldSize = new SGSlider( "World Size",200,1,5,300);
strokeThick = new SGSlider( "Thickness",4,1,1,20);
zoom = new SGLabel("Zoom", 1);
openFile = new SGButton(loadImage("stock_open-options-24.png"),"open", this);
saveFile = new SGButton(loadImage("stock_save-options-24.png"),"save",this);
move = new SGButton(loadImage("stock-tool-button-move.png"),"move",this);
zoomButton = new SGButton(loadImage("stock-tool-button-zoom.png"),"zoom",this);
draw = new SGButton(loadImage("stock_draw-freeform-line.png"),"draw",this);
fillDraw = new SGButton(loadImage("stock_draw-freeform-line-filled.png"),"fillDraw",this);
undo = new SGButton(loadImage("stock_undo-history-alt-24.png"),"undo",this);
enterText = new SGButton(loadImage("stock_draw-text.png"),"text",this);
snapLink = new SGButton(loadImage("stock_link.png"),"snapLink",this);
makeLink = new SGButton(loadImage("stock_form-pattern-field.png"),"makeLink",this);
newFile = new SGButton(loadImage("stock_new-html.png"),"new",this);
rot = new SGButton(loadImage("stock-tool-button-rotate.png"),"rotate",this);
settingsButton = new SGButton(loadImage("stock_form-properties.png"),"settings",this);
strok = new ColorBox();
fil = new ColorBox();
r = new SGSlider( "Stroke Red",0,5,0,255);
g = new SGSlider( "Stroke Green",0,5,0,255);
b = new SGSlider( "Stroke Blue",0,5,0,255);
 
fr = new SGSlider( "Fill Red",128,5,0,255);
fg = new SGSlider( "Fill Green",128,5,0,255);
fb = new SGSlider( "Fill Blue",200,5,0,255);
fa = new SGSlider( "Fill Alpha",255,5,0,255);
//settings.add(worldSize);
 
settings.add(strokeThick);
settings.add(tolerance);
settings.add(strok);
settings.add(r);
settings.add(g);
settings.add(b);
settings.add(fil);
settings.add(fr);
settings.add(fg);
settings.add(fb);
settings.add(fa);
settings.setVisible(0);
//settings.add(zoom);
controls.add(newFile);
controls.add(openFile);
controls.add(saveFile);
if (online) {
openFile.enabled = false;
saveFile.enabled = false;
}
controls.add(zoomButton);
controls.add(move);
controls.add(rot);
controls.add(draw);
controls.add(fillDraw);
controls.add(enterText);
controls.add(snapLink);
controls.add(makeLink);
makeLink.enabled = false;
controls.add(settingsButton);
controls.add(undo);
 
controller.add(settings);
controller.add(controls);
//cam = new SGPlanarCam(this,new LinearInterpolatedVar(this,0,20),new LinearInterpolatedVar(this,0,20),new FOLagInterpolatedVar(this,1,2),new LinearInterpolatedVar(this,0,20));
cam = new SGPlanarCam(this,new FOLagInterpolatedVar(this,0,4),new FOLagInterpolatedVar(this,0,4),new FOLagInterpolatedVar(this,1,2),new FOLagInterpolatedVar(this,0,4));
cam.setMouseMode(RIGHT,SGPlanarCam.ZOOM);
fillColor = color(fr.getVal(),fg.getVal(),fb.getVal(),fa.getVal());
clear();
baseCam = cam.getState();
 
if (online) readIn("hello.gz");
 
}
 
void draw() {
if (scriptQ != null){
readIn(scriptQ);
}
background(255);
penColor = color(r.getVal(),g.getVal(),b.getVal());
strok.setFillColor(penColor);
 
fillColor = color(fr.getVal(),fg.getVal(),fb.getVal(),fa.getVal());
fil.setFillColor(fillColor);
float sc = cam.getZoom();
float wrldSize = worldSize.getVal();
float delta = cam.sc.getTargetVal() - cam.sc.getVal();
if (sc > wrldSize){
 
cam.setZoom(-wrldSize);
cam.sc.setCurrVal(cam.sc.getTargetVal());
}
if (sc < -wrldSize){
cam.setZoom(wrldSize);
cam.sc.setCurrVal(cam.sc.getTargetVal());
}
//be sure to avoid negative loops in the interpolated variables
 
stroke(0);
zoom.setVal(cam.getZoom());
 
CameraState state = cam.getState();
float sc2 = cam.sc.getVal();
float sc3 = cam.sc.getTargetVal();
//before rendering, check for camera zoom wrap
//can the camera see "behind itself"
if ((sc - dof) < -wrldSize){
 
cam.setZoom(wrldSize + (sc + wrldSize));
cam.sc.setCurrVal(cam.sc.getTargetVal());
cam.feed();
vg.render(pen, cam);
 
//pop the camera values
 
cam.setState(state);
}
//can the camera see "behind itself"
if ((sc + dof) > wrldSize){
cam.setZoom(-wrldSize - ( wrldSize-sc));
cam.sc.setCurrVal(cam.sc.getTargetVal());
cam.feed();
 
vg.render(pen, cam);
 
//pop the camera values
cam.setState(state);
}
cam.sc.setCurrVal(sc2);
 
cam.feed();
vg.render(pen, cam);
 
picking = false;
if (clipBoard != null){
stroke(0,0,250);
strokeWeight(2.f/cam.getScale());
line(cam.cx.getVal(),cam.cy.getVal(),clipBoard.cx,clipBoard.cy);
}
}
 
 
 
void clear(){
vg = new VectorGraphic(this);
camHandle = new CamHandler(this);
linkHandle = new LinkHandler(this);
txtHandle = new TextHandler(this);
vg.addTokenHandler(txtHandle);
vg.addTokenHandler(linkHandle);
 
 
Vector settings = new Vector(2);
settings.add("th");
settings.add("" + pen.strokeWeight);
vg.vector.add(settings);
Vector settings2 = new Vector(6);
settings2.add("sc");
settings2.add("" + (cam.getZoom()));
Tuple2f [] bounds = cam.getBoundingBox();
settings2.add(bounds[0]);
settings2.add(bounds[1]);
settings2.add("-1");
settings2.add(cam.cx.getVal() + "");
settings2.add(cam.cy.getVal() + "");
settings2.add("" + (strokeThick.getVal()/cam.getScale()));
settings2.add(0 + "");
vg.vector.add(settings2);
vg.lastScale = settings2;
 
Vector settings3 = new Vector(2);
settings3.add("fo");
settings3.add("" + fillColor);
vg.vector.add(settings3);
 
Vector settings4 = new Vector(2);
settings4.add("co");
settings4.add("" + penColor);
vg.vector.add(settings4);
 
}
void mousePressed(){
 
if (!mouseEvent.isConsumed() && mouseButton == LEFT){
if (currentStroke == null && (mode == DRAW_LINE || mode == FILL_LINE || mode == DRAW_LINK)){
//first -- do we need to send any stroke formatting commands?
//if the currentStroke doesn't match the gui val drop a change
if (pen.strokeWeight != strokeThick.getVal()/cam.getScale()){
Vector settings = new Vector(2);
settings.add("th");
settings.add("" + (strokeThick.getVal()/cam.getScale()));
vg.vector.add(settings);
}
validateCameraBox();
//mark color changes
if (pen.strokeColor != penColor){
Vector settings = new Vector(2);
settings.add("co");
settings.add(penColor + "");
vg.vector.add(settings);
}
 
//mark fill color changes
else if (pen.fill != fillColor){
Vector settings = new Vector(2);
settings.add("fo");
settings.add(fillColor + "");
vg.vector.add(settings);
}
 
println("creating current stroke");
currentStroke = new Vector();
if (mode == DRAW_LINE || mode == DRAW_LINK) currentStroke.add("st");
else if (mode == FILL_LINE) currentStroke.add("fs");
vg.vector.add(currentStroke);
}
}
}
 
void mouseClicked(){
picking = true;
}
 
void mouseDragged(){
//don't draw the line if the viewcamera ate it
if (!mouseEvent.isConsumed()){
if (mode == TEXT_ENTRY){
 
int lx = Integer.parseInt((String)txEnter.elementAt(2));
lx += (mouseX-pmouseX)/cam.sc.getVal();
int ly = Integer.parseInt((String)txEnter.elementAt(3));
ly += (mouseY-pmouseY)/cam.sc.getVal();
txEnter.set(3,ly + "");
txEnter.set(2,lx + "");
}
else{
if (currentStroke != null) currentStroke.add(cam.screenToWorld(mouseX,mouseY));
}
}
 
}
 
 
 
void mouseReleased(){
 
if (!mouseEvent.isConsumed() && currentStroke != null){
//simplify the current stroke
Tuple2f [] tmp = new Tuple2f[currentStroke.size()-1];
for (int i =0; i < currentStroke.size()-1; i++){
tmp[i] = (Tuple2f)currentStroke.elementAt(i+1);
}
//pull the working line out, simplify it, and drop it back in later
vg.vector.removeElementAt(vg.vector.size()-1);
 
Vector toAdd = new Vector();
if (currentStroke.size() != 1){
if (mode == DRAW_LINE || mode == FILL_LINE ){
if (mode == DRAW_LINE) toAdd.add("st");
else if (mode == FILL_LINE) toAdd.add("fs");
// reduce the complexity of the line
// be sure to scale the tolerance by the camera zoom.
toAdd.addAll(Arrays.asList(SGImplify.simplifyLine2D(tolerance.getVal()/cam.getScale(),tmp)));
vg.vector.add(toAdd);
}
else if (mode == DRAW_LINK && clipBoard != null){
toAdd.add("ln");
//convert the draw array to a general path
TokenPath path = new TokenPath();
Tuple2f [] smp = SGImplify.simplifyLine2D(tolerance.getVal()/cam.getScale(),tmp);
path.moveTo(smp[0].x,smp[0].y);
for (int i =1; i < smp.length; i++){
path.lineTo(smp[i].x,smp[i].y);
}
toAdd.add(path);
toAdd.add(clipBoard);
vg.vector.add(toAdd);
clipBoard = null;
draw.enabled = true;
fillDraw.enabled = true;
makeLink.enabled = false;
undo.enabled = true;
enterText.enabled = true;
}
}
}
//close entry on the current stroke
currentStroke = null;
}
 
void undo(){
vg.vector.removeElementAt(vg.vector.size()-1);
 
}
 
 
 
//me:This is a terrible way of doing this....
//critic: Why don't you think of a more robust way of doing it?
//me: I'm very set in my ways.
 
void GUIEventPerformed(GUIEvent e){
 
if (mode == TEXT_ENTRY){
if (mode == TEXT_ENTRY) mode = NONE;
picking = true;
 
}
keyCode = 0;
if (e.title.equals("draw")){
key = 'd';
keyPressed();
}
if (e.title.equals("fillDraw")){
 
key = 'f';
keyPressed();
}
else if (e.title.equals("zoom")){
key = 'y';
keyPressed();
}
else if (e.title.equals("move")){
key = 't';
keyPressed();
}
else if (e.title.equals("open")){
key = 'o';
keyPressed();
}
else if (e.title.equals("save")){
key = 's';
keyPressed();
}
else if (e.title.equals("undo")){
undo();
}
else if (e.title.equals("text")){
key = 'e';
keyPressed();
}
else if (e.title.equals("snapLink")){
key = 'c';
keyPressed();
 
}
else if (e.title.equals("makeLink")){
key = 'l';
keyPressed();
}
else if (e.title.equals("new")){
key = 'n';
keyPressed();
}
else if (e.title.equals("rotate")){
key = 'r';
 
keyPressed();
}
else if (e.title.equals("settings")){
settings.toggleVisible();
}
}
 
void keyPressed(){
 
if (key == CODED){
modKey = keyCode;
}
if (modKey == SHIFT){
if (keyCode > 48 && keyCode < 58){
camHandle.savedStates[keyCode-49] = cam.getState();
}
}
 
//ctrl+z
else if (modKey == CONTROL){
if (keyCode == 90) undo();
}
if (mode == TEXT_ENTRY){
if (keyCode == ENTER){
txt.append("\f");
}
 
else if (keyCode == BACKSPACE){
if (txt.length() > 0) txt.deleteCharAt(txt.length()-1);
}
else if (keyCode == SHIFT){
 
}
 
else{
txt.append(key);
}
}
else{
if (keyCode > 48 && keyCode < 58){
cam.setState(camHandle.savedStates[keyCode-49]);
}
else if (key == 'l'){
cam.setMouseMode(LEFT,SGPlanarCam.UNBOUND);
mode = DRAW_LINK;
}
 
else if (key == 'c'){
clipBoard = cam.getState();
mode = NONE;
makeLink.enabled = true;
draw.enabled = false;
fillDraw.enabled = false;
enterText.enabled = false;
undo.enabled = false;
cam.setMouseMode(LEFT,SGPlanarCam.TRANSLATE);
}
else if (key == 't'){
cam.setMouseMode(LEFT,SGPlanarCam.TRANSLATE);
mode = TRANSLATE;
}
else if (key == 'y'){
cam.setMouseMode(LEFT,SGPlanarCam.ZOOM);
mode = ZOOM;
}
else if (key == 'r'){
cam.setMouseMode(LEFT,SGPlanarCam.ROTATE);
mode = ROTATE;
}
else if (key == 'd'){
cam.setMouseMode(LEFT,SGPlanarCam.UNBOUND);
mode = DRAW_LINE;
}
else if (key == 'f'){
cam.setMouseMode(LEFT,SGPlanarCam.UNBOUND);
mode = FILL_LINE;
}
else if (key == 'n'){
clear();
}
else if (key == 's'){
writeOut();
}
else if (key == 'o'){
readIn();
}
else if (key == 'q'){
loadFile("base.gz");
}
else if (key == 'a'){
cam.zoom(3);
}
else if (key == 'z'){
cam.zoom(-3);
}
else if (key == 'e'){
 
validateCameraBox();
mode = TEXT_ENTRY;
cam.setMouseMode(LEFT,SGPlanarCam.UNBOUND);
txt = new StringBuffer("");
txEnter = new Vector(6);
txEnter.add("tx");
txEnter.add(cam.getScale() + "");
txEnter.add((int)cam.cx.getTargetVal() + "");
txEnter.add((int)cam.cy.getTargetVal() + "");
txEnter.add(cam.rot.getVal() + "");
txEnter.add(txt);
vg.vector.add(txEnter);
}
}
 
}
 
void validateCameraBox(){
 
//if the person has moved the camera recently, drop a new camera box
if (pen.scale != cam.getZoom() || abs(pen.cx - cam.cx.getTargetVal()) > .1 || abs(pen.cy - cam.cy.getTargetVal()) > .1 ){
println("dropping scale");
Vector settings = new Vector(6);
settings.add("sc");
settings.add("" + (cam.getZoom()));
Tuple2f [] bounds = cam.getBoundingBox();
settings.add(bounds[0]);
settings.add(bounds[1]);
settings.add("-1");
settings.add(cam.cx.getVal() + "");
settings.add(cam.cy.getVal() + "");
settings.add("" + (strokeThick.getVal()/cam.getScale()));
settings.add(penColor + "");
settings.add(fillColor + "");
vg.vector.add(settings);
vg.lastScale.setElementAt(vg.vector.size() + "",4);
vg.lastScale = settings;
}
 
}
 
 
void keyReleased(){
if (keyCode == CONTROL || keyCode == SHIFT) modKey = 0;
}
 
 
void writeOut(){
//find where to save
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
JFileChooser fc = new JFileChooser();
int returnVal = fc.showSaveDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File selFile = fc.getSelectedFile();
if (selFile != null){
String outName = selFile.getAbsolutePath();
Vector a;
String [] output = new String[vg.vector.size() + 1];
for(int i =0; i < vg.vector.size();i++ ){
a = (Vector)vg.vector.elementAt(i);
output[i] = a.toString();
}
//pump the camera states
output[vg.vector.size()] = camHandle.tokenize();
if (!outName.endsWith(".gz")) outName = outName + ".gz";
saveStrings(outName, output);
 
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
 
void readIn(){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new ZipFilter());
int returnVal = fc.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File selFile = fc.getSelectedFile();
if (selFile != null){
String outName = selFile.getAbsolutePath();
readIn(outName);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
 
private void readIn(String fileName){
Vector handlers = new Vector();
handlers.add(txtHandle);
handlers.add(linkHandle);
handlers.add(camHandle);
vg = new VectorGraphic(this,fileName, handlers);
vg.addTokenHandler(txtHandle);
vg.addTokenHandler(linkHandle);
scriptQ = null;
cam.setState(baseCam);
}
 
void loadFile(String filename){
 
try{
 
URL base = new URL(getCodeBase(),filename);
scriptQ = base.toString();
}
catch (Exception e){
println("couldn't open file." + e );
}
}
 
 
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
 
class TextHandler implements ExternalTokenHandler{
 
PApplet parent;
Graphics2D g2d;
 
public TextHandler(PApplet parent){
this.parent = parent;
}
 
public void parseFile(VectorGraphic vg, String header, StringTokenizer st){
if (header.equals("tx")){
Vector toAdd = new Vector(st.countTokens());
toAdd.add(header);
for (int i =0; i < 4; i++){
toAdd.add(st.nextToken());
}
StringBuffer buff = new StringBuffer();
while(st.hasMoreTokens()){
buff.append(st.nextToken() + " " );
 
}
toAdd.add(buff);
vg.vector.add(toAdd);
}
}
public void renderGraphic(VectorGraphic vg, String header, Vector cStroke){
fill(0);
stroke(0);
 
textMode(MODEL);
//text("banana",0,0);
if (header.equals("tx")){
g2d = ((PGraphicsJava2D)parent.g).g2;
g2d.setColor(new Color(0,0,0));
float wt = new Float((String)cStroke.elementAt(1)).floatValue();
int x = new Integer((String)cStroke.elementAt(2)).intValue();
int y = new Integer((String)cStroke.elementAt(3)).intValue();
float rot = new Float((String)cStroke.elementAt(4)).floatValue();
StringBuffer text = (StringBuffer)cStroke.elementAt(5);
Font font = new Font("SansSerif", Font.BOLD,(int)(16.f/wt));
String [] lines = text.toString().split("\f");
g2d.setFont(font);
AffineTransform aff = g2d.getTransform();
if (aff.getScaleX() < 15 ){
for (int j =0; j < lines.length; j++){
 
g2d.translate(x,y);
g2d.rotate(-rot);
g2d.translate(0,15.f/wt*j);
g2d.drawString(lines[j],0,0);
//text(lines[j],0,0);
g2d.setTransform(aff);
}
}
}
}
 
}
 
 
class TokenPath extends Path2D.Float{
 
public TokenPath(String token){
String [] pts = token.split("/");
 
Tuple2f tmp = new Tuple2f(pts[0]);
moveTo(tmp.x,tmp.y);
for (int i =1; i < pts.length; i++){
tmp = new Tuple2f(pts[i]);
lineTo(tmp.x,tmp.y);
}
}
public TokenPath(){
}
public String toString(){
PathIterator pi = getPathIterator(null);
float[] coords = new float[2];
String toRet = "";
while(!pi.isDone()){
pi.currentSegment(coords);
toRet += coords[0] + ":" + coords[1] + "/";
pi.next();
}
return toRet;
}
 
}
public class ZipFilter extends javax.swing.filechooser.FileFilter {
 
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
 
String extension = getExtension(f);
if (extension != null) {
if (extension.equals("gz")) {
return true;
} else {
return false;
}
}
 
return false;
}
public String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
 
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i+1).toLowerCase();
}
return ext;
}
//The description of this filter
public String getDescription() {
return "GZip Compressed Files";
}
}