FileSystemUtils.java

/***************************************************************************
   Copyright 2014 Emily Estes

   Licensed 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 net.metanotion.io;


import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/** This is a utility class for handling file names to make it simpler to correctly implement the
{@link net.metanotion.io.FileSystem} interface. */
public final class FileSystemUtils {
	/** Join a list into a string file name using the default file separator.
		@param file The filename to join.
		@return A string for the file name.
	*/
	public static String join(final List<String> file) { return join(file, FileSystem.SEPARATOR); }

	/** Join a list into a string file name.
		@param file The filename to join.
		@param separator The path component separator.
		@return A string for the file name.
	*/
	public static String join(final List<String> file, final String separator) {
		final StringBuilder b = new StringBuilder();
		for(String s: file) {
			b.append(separator);
			b.append(s);
		}
		return b.toString();
	}

	/** Split a root file name into it's components using the default separator.
		@param base The filename to split.
		@return A list of the file components.
	*/
	public static List<String> getRoot(String base) { return getRoot(base, FileSystem.SEPARATOR); }

	/** Split a root file name into it's components using the default separator.
		@param base The filename to split.
		@param separator The path component separator.
		@return A list of the file components.
	*/
	public static List<String> getRoot(String base, String separator) {
		if(base == null) { base = separator; }
		if(base.length() == 0) { base = separator; }
		return FileSystemUtils.parse(base);
	}

	/** Compare two lists to see if the second list has the same components as the first list for it's prefix.
		@param prefix The common prefix.
		@param list The list to compare.
		@return True list starts with prefix, false otherwise.
	*/
	public static boolean isPrefix(List<String> prefix, List<String> list) {
		if(list.size() < prefix.size()) { return false; }
		final Iterator it = list.iterator();
		for(final String s: prefix) {
			if(!s.equals(it.next())) { return false; }
		}
		return true;
	}

	/** Parse a filename into it's components and make sure it doesn't escape it's root.
		@param url Filename to parse.
		@param root The root this file cannont escape.
		@return The parsed components of the file.
		@throws RuntimeException if the file escapes it's bounds.
	*/
	public static List<String> getFile(String url, List<String> root) {
		final LinkedList<String> file = new LinkedList<String>();
		file.addAll(root);
		file.addAll(FileSystemUtils.parse(url));
		if(!isPrefix(root, file)) { throw new RuntimeException("File is outside of root path of FileSystem"); }
		return file;
	}

	/** Split a file into it's component parts.
		@param url The file name to parse.
		@return The list of it's components.
	*/
	public static List<String> parse(final String url) {
		final LinkedList<String> list = new LinkedList<>();
		final String[] t = url.split(FileSystem.SEPARATOR);
		for(String p: t) {
			if("".equals(p)) { continue; }
			if(".".equals(p)) { continue; }
			if("..".equals(p)) {
				final int s = list.size();
				if(s > 0) { list.remove(s - 1); }
			} else {
				list.add(p);
			}
		}
		return list;
	}
}