Skip to content

File Management

Hempfest edited this page Dec 19, 2021 · 10 revisions

Initialize file listing

The first step to accessing any files is going to be creation of the listing for them. Let's start by initializing our list on our plugins start up.

private FileList files;

public void onEnable() {
files = FileList.search(this);
}

public FileList getFileList() {
return files;
}

Creating a new file

Now that we have our listing initialized we can start looking for files to work with. Lets use the test file for now just for this tutorial, we also have the option of using either YAML or JSON formatting.

FileManager manager = files.get("test"); // We can add optional directory locations by adding a comma after "test"
// Can also use files.get("test", FileType.JSON);, no file type provided defaults to YAML.

Since this file is brand new we know it doesnt exist so lets run our check and create it.

if (!manager.getRoot().exists()) {
   manager.getRoot().create();
}

Now accessing objects from the file is as easy as:

ItemStack item = manager.read(c -> c.getItemStack("my-item"));
String test = manager.read(c -> c.getString("my-key"));

Newly introducing the Node system:

FileManager manager = files.get("test", "My/Directory");
// These nodes layed out represent the total path "start.key1" but can go as deep as you want!
Node start = manager.getRoot().getNode("start");
Node key1 = start.getNode("key1");
Object value1 = key1.get();

Pre allocating value storage without direct writing can be accomplished using whats known as a DataTable It works by allowing you to attach any value to any key you want without actually consuming the resources for the file write.

Simply fill a data table:

DataTable table = DataTable.newTable();
table.set("my.key", Object);

Once you have your data table ready to be written to your file, grab your manager instance and pass your table into the write method like so:

manager.write(table);

No further reloading/saving necessary all pre allocated data is now written directly to the file.

Copying files

You can copy files you need from input streams, then specify output file locations.

public void onEnable() {

File test = new File("My/Directory");
FileManager manager = files.get("test", "My/Directory");
if (!manager.getRoot().exists()) {
    files.copyYML("test", test);
}

}

Special File Types

Know how to parse your own data from an unprovided file type? No problem! Use our Configurable abstraction to delegate all the information!

// use the optional to work with custom files.
FileExtension extension = MY_EXTENSION;
CustomFileOptional optional = files.check("test", null, extension);
if (optional.isPresent()) {
	// do stuff
} else {
	// do other stuff
	files.inject(Configurable);
}

// Or get right to the chase, if no configurable provider is found for the given extension provide one!
FileManager manager = files.check("test", "My/Directory", extension).orElse(Configurable);

Json Adapters

You can write what we call "adapters" for custom data conversions to json.

This is a required step if you plan on using the JSON file type provided by Labyrinth for your file management.

To explain this we can simply take a look at the default provision for org.bukkit.Location through a json adapter.

@NodePointer("org.bukkit.Location")
public final class LocationSerializable implements JsonAdapter<Location> {

	@Override
	public JsonElement write(Location l) {
		JsonObject o = new JsonObject();
		o.addProperty("x", l.getX());
		o.addProperty("y", l.getY());
		o.addProperty("z", l.getZ());
		o.addProperty("yaw", l.getYaw());
		o.addProperty("pitch", l.getPitch());
		o.addProperty("world", l.getWorld().getName());
		return o;
	}

	@Override
	public Location read(Map<String, Object> o) {
		String world;
		double x, y, z;
		float yaw, pitch;
		world = (String) o.get("world");
		x = (double) o.get("x");
		y = (double) o.get("y");
		z = (double) o.get("z");
		yaw = (float) (double) o.get("yaw");
		pitch = (float) (double) o.get("pitch");
		return new Location(Bukkit.getWorld(world), x, y, z, yaw, pitch);
	}

	@Override
	public Class<Location> getClassType() {
		return Location.class;
	}
}

We have ourselves a @NodePointer that's responsible for telling a MemorySpace the type of object our keyed values represents, more informating regarding exacts on a node pointer can be found within the javadocs.

As you can see the adapter contains three important things A conversion TO json method, a conversion FROM json method and the class reference of the object being translated.

Once you have your adapter made you can access it from anywhere using the utility static method Configurable#getAdapter(Class<T>)