/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loïc Le Coq * Copyright (C) 2013 Marko Zivkovic * * Contact Information: marko88zivkovic at gmail dot com * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. You should have received a copy of the * GNU General Public License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic * during his Bachelor thesis at the computer science department of ETH Zürich, * in the year 2013 and/or during future work. * * It is a reengineered version of XLogo written by Loïc Le Coq, published * under the GPL License at http://xlogo.tuxfamily.org/ * * Contents of this file were entirely written by Marko Zivkovic */ package xlogo.storage; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.StringTokenizer; public abstract class Storable implements Serializable { /** * */ private static final long serialVersionUID = 3506253939129765438L; /** * The file's name with extension */ private String fileName; /** * The Directory where this is stored */ private File location; /** * Dirty : an object is dirty if it was changed since it was loaded or stored the last time. */ private transient boolean dirty = true; /** * Will not be stored if virtual. */ private transient boolean isVirtual = false; /* * PATH BUILDERS */ public static File getFile(File dir, String fileName) { return new File(dir.toString() + File.separator + fileName); } public static File getDirectory(File prefix, String dirName) { return new File(prefix.toString() + File.separator + dirName); } /* * Abstract */ /** * Store this object to the file specified by {@link #getFilePath()} if it is dirty * @throws IOException */ public abstract void store() throws IOException; public abstract void storeCopyToFile(File file) throws IOException, IllegalArgumentException; /** * Store this object to the specified file, regardless of whether this is virtual or not. * @param file * @throws IOException * @throws IllegalArgumentException - null is not accepted */ /* * file name & location */ public abstract String getFileNameExtension(); public String getFileName() { return getPlainName() + getFileNameExtension(); } /** * @return FileName without file extension */ public String getPlainName() { return fileName; } /** * If this exists on the file system, that file will be renamed.
* If newFileName already existed, it is deleted first.
* @param newFileName
* @throws IllegalArgumentException - If the provided name is not legal.
*/
public void setFileName(String newFileName) throws IllegalArgumentException //TODO make sure callers conform with contract
{
if (newFileName == null || newFileName.length() == 0)
throw new IllegalArgumentException("File name must not be null or empty.");
if (!checkLegalName(newFileName))
throw new IllegalArgumentException("The chose file name contains illegal characters.");
String ext = getFileNameExtension();
String oldName = getPlainName();
String newName = newFileName.endsWith(ext) && newFileName.length() > ext.length() ?
newFileName.substring(0, newFileName.length() - ext.length())
: newFileName;
if (newName.equals(oldName) && oldName != null)
return;
if (isVirtual || oldName == null)
{
this.fileName = newFileName;
return;
}
File oldPath = getFilePath();
this.fileName = newName;
if (!oldPath.exists())
return;
File newPath = getFilePath();
if(newPath.exists())
newPath.delete();
oldPath.renameTo(newPath);
}
/**
* @return the directory where this should be stored to.
*/
public File getLocation()
{
return location;
}
/**
* If the specified location does not exist yet, it is created using mkdirs.
* To set null or a file that is not a directory or a directory with no write permissions is an error, as long as this is not virtual.
* Setting location has no effect if this is virtual.
* @param location - the directory where this should be stored to.
* @throws IOException
* @throws IOException If the specified location is not a directory or no write permissions exist, or the chosen name is not legal.
*/
public void setLocation(File location) throws IllegalArgumentException
{
if(isVirtual)
return;
if (location == null)
throw new IllegalArgumentException("Location must not be null.");
if(!location.isDirectory())
{
location.mkdirs();
}
if(!location.isDirectory() || !location.canWrite())
throw new IllegalArgumentException("Cannot store this to specified location : " + location.toString());
this.location = location;
makeDirty();
}
/**
* @return the file where this should be stored to. Returns null if {@link getLocation()} returns null.
*/
public File getFilePath()
{
if (getLocation() == null)
return null;
return getFile(getLocation(), getFileName());
}
/**
* @return whether the file specified by {@link #getFilePath()} exists.
*/
public boolean existsPhysically()
{
if (getFilePath() == null)
return false;
return getFilePath().exists();
}
/*
* isDirty
*/
public boolean isDirty() { return dirty; }
/**
* Should be called from every setter that sets a property that should be stored later
* @see StorableObject#makeClean()
*/
protected void makeDirty() { dirty = true; }
/**
* Should be called whenever this was synchronized with its version on the file system (load or store)
* @see StorableObject#makeDirty()
*/
protected void makeClean() { dirty = false; }
/*
* isVirtual
*/
/**
* @see #isVirtual()
*/
protected void makeVirtual() { isVirtual = true; }
/**
* A virtual object will not be stored on the file system, even though {@link store()} was called.
* This allows to use the application without having an actual user account and without automatic saving.
* @return
*/
public boolean isVirtual() { return isVirtual; }
// The best I found : http://stackoverflow.com/questions/893977/java-how-to-find-out-whether-a-file-name-is-valid
// some windows specific chars are not contained...
public static final String ILLEGAL_NAME_CHARACTERS = "/\n\r\t\0\f`?*\\<>|\":";
public static boolean checkLegalName(String name)
{
if (name == null || name.length() == 0)
return false;
//StringTokenizer check = new StringTokenizer(name, ILLEGAL_NAME_CHARACTERS, true);
//return (check.countTokens() == 1);
for(char c : name.toCharArray())
{
if (ILLEGAL_NAME_CHARACTERS.indexOf(c) > -1)
return false;
}
return true;
}
}