XanderFileUtil

From Robowiki
Revision as of 07:28, 11 November 2011 by Skotty (talk | contribs) (→‎Usage: Provide more details on usage (describe what the arguments are))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Introduction

The Xander File Utility is a single class, XanderFileUtil, that makes it easier to handle loading and saving data from and to disk across multiple versions of your robot.

A word of caution -- this utility class is not designed to handle the situation where you run a robot against itself.

Consider it freeware. Use it as you like. The full source will be posted below in the next day or two.

Usage

The two main methods of this utility class are:

public static void save(
	Serializable o,
	AdvancedRobot robot, 
	String fileNamePrefix, 
	boolean separateByVersion, 
	boolean allowOverwriteOfNewerVersions)

public static <T> T load(
	AdvancedRobot robot, 
	String fileNamePrefix, 
	boolean separateByVersion, 
	boolean allowReadFromOtherVersions)

The arguments are as follows:

  • Serializable o -- the object to save to file
  • AdvancedRobot robot -- the robot object
  • String fileNamePrefix -- main part of file name, minus the extension.
  • boolean separateByVersion -- whether or not to use separate files for each robot version. When true, a version identifier is appended to the file name to keep the file name unique to the version of the robot. When false, the same file name is used for every version of the robot.
  • boolean allowOverwriteOfNewerVersions -- applies when saving and separateByVersion is set to false; indicates whether or not to allow older robot versions to be able to replace files for newer robot versions. Files written by older versions will always be overwritten.
  • boolean allowReadFromOtherVersions -- applies when loading and separateByVersion is set to false; indicates whether or not to allow the robot to attempt to load files created by other versions of the robot.

Robot Version Support

Determining whether a robot version is an earlier or later version from another is done by examining the version string. The version string can consist of one or more character sequences separated by periods, as in the following examples:

  • 7
  • 1.5.0
  • 10.7c
  • 3ab.4cd

When comparing a component of the version string, if that component is numeric, it is compared as a number; otherwise, it is compared as a String (e.g. version "1.10" will be considered newer than version "1.2", which wouldn't happen if the components were compared as Strings). In cases where a two versions have the same initial version string components, but one version has more components than the other, the version with more components is considered a newer version (e.g. version "1.2.0" would be considered newer than version "1.2").

Source Code

This source code is currently untested! So use at your own peril.

I'll be testing it in the next couple of days.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import robocode.AdvancedRobot;
import robocode.RobocodeFileOutputStream;

/**
 * Generic Robocode utility for saving and loading data from the file system.
 *
 * WARNING!  This utility class has not been written to handle the situation 
 * where you pit a robot against itself!
 **/
public class XanderFileUtil {

	private static final String EXT = ".rcd";   // (r)obo(c)ode (d)ata

	public static class RobotVersion implements Serializable, Comparable<RobotVersion> {
		
		private static final long serialVersionUID = 2011111001;
		
		private String[] versionComponents;

		public RobotVersion(String robotName) {
			this.versionComponents = XanderFileUtil.getRobotVersionString(robotName).split("\\.");
		}

		private Integer getInteger(String versionComponent) {
			try {
				return Integer.valueOf(versionComponent);
			} catch (Exception e) {
				return null;
			}
		}

		@Override
		public int compareTo(RobotVersion other) {
			int result = 0;
			int i = 0;
			while (result == 0 && i < versionComponents.length && i < other.versionComponents.length) {
				Integer numericVC = getInteger(versionComponents[i]);
				Integer otherNumericVC = getInteger(other.versionComponents[i]);
				if (numericVC == null || otherNumericVC == null) {
					result = versionComponents[i].compareTo(other.versionComponents[i]);
				} else {
					result = numericVC.intValue() - otherNumericVC.intValue();
				}
				i++;
			}
			if (result == 0) {
				result = versionComponents.length - other.versionComponents.length;
			}
			return result;
		}

		public String toString() {
			StringBuilder sb = new StringBuilder();
			for (String versionComponent : versionComponents) {
				sb.append('_').append(versionComponent);
			}
			return sb.toString();
		}
	}

	public static class RobocodeFileObject implements Serializable {

		private static final long serialVersionUID = 2011111001;

		private RobotVersion version;
		private Serializable object;

		public RobocodeFileObject(Serializable object, String robotName) {
			this.object = object;
			this.version = new RobotVersion(robotName);
		}

	}

	private static String getFileNameWithoutVersion(String fileNamePrefix) {
		return fileNamePrefix + EXT;
	}
	
	private static String getFileNameWithVersion(RobotVersion robotVersion, String fileNamePrefix) {
		return fileNamePrefix + robotVersion.toString() + EXT;
	}

	/**
	 * Returns just the version part of the robot name.
	 * 
	 * @param robotName   full robot name
	 * 
	 * @return            robot version string
	 */
	public static String getRobotVersionString(String robotName) {
		String strings[] = robotName.split(" ");
		return strings[strings.length-1];
	}

	private static RobotVersion readVersion(File file) throws IOException {
		return readRobocodeFileObject(file).version;
	}

	private static RobocodeFileObject readRobocodeFileObject(File file) throws IOException {
		RobocodeFileObject rcfo = null;
		ObjectInput oi = null;
		try {
			ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
			zis.getNextEntry();
			oi = new ObjectInputStream(zis);
			rcfo = (RobocodeFileObject) oi.readObject();
			oi.close();
		} catch (ClassNotFoundException e) {
			// This should never happen; to make things easier, just wrap it in an IOException
			throw new IOException(e.getMessage(), e);
		} finally {
			if (oi != null) {
				try {
					oi.close();
				} catch (Exception e) { }
			}
		}
		return rcfo;
	}

	/**
	 * Save object to disk.
	 * 
	 * @param o                                the object to save
	 * @param robot                            the robot object instance
	 * @param fileNamePrefix                   the basic name for the file
	 * @param separateByVersion                whether or not to use separate files for different versions of your robot
	 * @param allowOverwriteOfNewerVersions    when not separating files by version, whether or not it should be allowed for an older version of the robot to overwrite a file written by a newer version.
	 * 
	 * @throws IOException
	 */
	public static void save(Serializable o, AdvancedRobot robot, String fileNamePrefix, boolean separateByVersion, boolean allowOverwriteOfNewerVersions) throws IOException {
		String robotName = robot.getName();
		RobocodeFileObject rcfo = new RobocodeFileObject(o, robotName);
		String fileName = null;
		if (separateByVersion) {
			fileName = getFileNameWithVersion(rcfo.version, fileNamePrefix);
		} else {
			fileName = getFileNameWithoutVersion(fileNamePrefix);
		}
		File file = robot.getDataFile(fileName);
		if (!separateByVersion && !allowOverwriteOfNewerVersions && file.exists()) {
			RobotVersion otherVersion = readVersion(file);
			if (rcfo.version.compareTo(otherVersion) < 0) {
				System.out.println("A version of the file from a newer version of the robot already exists and will not be overwritten.");
				return;
			}
		}
		ObjectOutput oo = null;
		try {
			ZipOutputStream zos = new ZipOutputStream(new RobocodeFileOutputStream(file));
			zos.putNextEntry(new ZipEntry(fileNamePrefix));
			oo = new ObjectOutputStream(zos);
			oo.writeObject(rcfo);
		} finally {
			if (oo != null) {
				try {
					oo.close();
				} catch (Exception e) { }
			}
		}
	}

	/**
	 * Load object from disk.
	 * 
	 * @param <T>                          type of object being loaded
	 * @param robot                        robot object instance
	 * @param fileNamePrefix               basic name of file
	 * @param separateByVersion            whether or not to use separate files for different versions of your robot
	 * @param allowReadFromOtherVersions   when not separating files by version, whether it should be allowed to read a file written by a different version of the robot
	 * 
	 * @return       object to be loaded.
	 * 
	 * @throws IOException
	 */
	public static <T> T load(AdvancedRobot robot, String fileNamePrefix, boolean separateByVersion, boolean allowReadFromOtherVersions) throws IOException {
		String robotName = robot.getName();
		String fileName = null;
		if (separateByVersion) {
			fileName = getFileNameWithVersion(new RobotVersion(robotName), fileNamePrefix);
		} else {
			fileName = getFileNameWithoutVersion(fileNamePrefix);
		}
		File file = robot.getDataFile(fileName);
		if (!file.exists()) {
			return null;
		}
		RobocodeFileObject rcfo = readRobocodeFileObject(file);
		if (!separateByVersion && !allowReadFromOtherVersions) {
			RobotVersion robotVersion = new RobotVersion(robotName);
			if (robotVersion.compareTo(rcfo.version) != 0) {
				return null;
			}
		}
		return (T) rcfo.object;
	}
}