209 lines
6.7 KiB
Vala
209 lines
6.7 KiB
Vala
/* Copyright 2011-2013 Yorba Foundation
|
|
*
|
|
* This software is licensed under the GNU Lesser General Public License
|
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
|
*/
|
|
|
|
public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
|
|
Gee.Comparable<Geary.FolderPath> {
|
|
public string basename { get; private set; }
|
|
|
|
private Gee.List<Geary.FolderPath>? path = null;
|
|
private string? fullpath = null;
|
|
private string? fullpath_separator = null;
|
|
private uint stored_hash = uint.MAX;
|
|
|
|
protected FolderPath(string basename) {
|
|
assert(this is FolderRoot);
|
|
|
|
this.basename = basename;
|
|
}
|
|
|
|
private FolderPath.child(Gee.List<Geary.FolderPath> path, string basename) {
|
|
assert(path[0] is FolderRoot);
|
|
|
|
this.path = path;
|
|
this.basename = basename;
|
|
}
|
|
|
|
public bool is_root() {
|
|
return (path == null || path.size == 0);
|
|
}
|
|
|
|
public Geary.FolderRoot get_root() {
|
|
return (FolderRoot) ((path != null && path.size > 0) ? path[0] : this);
|
|
}
|
|
|
|
public Geary.FolderPath? get_parent() {
|
|
return (path != null && path.size > 0) ? path.last() : null;
|
|
}
|
|
|
|
public int get_path_length() {
|
|
// include self, which is not stored in the path list
|
|
return (path != null) ? path.size + 1 : 1;
|
|
}
|
|
|
|
/**
|
|
* Returns null if index is out of bounds. There is always at least one element in the path,
|
|
* namely this one.
|
|
*/
|
|
public Geary.FolderPath? get_folder_at(int index) {
|
|
// include self, which is not stored in the path list ... essentially, this logic makes it
|
|
// look like "this" is stored at the end of the path list
|
|
if (path == null)
|
|
return (index == 0) ? this : null;
|
|
|
|
int length = path.size;
|
|
if (index < length)
|
|
return path[index];
|
|
|
|
if (index == length)
|
|
return this;
|
|
|
|
return null;
|
|
}
|
|
|
|
public Gee.List<string> as_list() {
|
|
Gee.List<string> list = new Gee.ArrayList<string>();
|
|
|
|
if (path != null) {
|
|
foreach (Geary.FolderPath folder in path)
|
|
list.add(folder.basename);
|
|
}
|
|
|
|
list.add(basename);
|
|
|
|
return list;
|
|
}
|
|
|
|
public Geary.FolderPath get_child(string basename) {
|
|
// Build the child's path, which is this node's path plus this node
|
|
Gee.List<FolderPath> child_path = new Gee.ArrayList<FolderPath>();
|
|
if (path != null)
|
|
child_path.add_all(path);
|
|
child_path.add(this);
|
|
|
|
return new FolderPath.child(child_path, basename);
|
|
}
|
|
|
|
public string get_fullpath(string? use_separator = null) {
|
|
string? separator = use_separator ?? get_root().default_separator;
|
|
|
|
// no separator, no hierarchy
|
|
if (separator == null)
|
|
return basename;
|
|
|
|
if (fullpath != null && fullpath_separator == separator)
|
|
return fullpath;
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
if (path != null) {
|
|
foreach (Geary.FolderPath folder in path) {
|
|
builder.append(folder.basename);
|
|
builder.append(separator);
|
|
}
|
|
}
|
|
|
|
builder.append(basename);
|
|
|
|
fullpath = builder.str;
|
|
fullpath_separator = separator;
|
|
|
|
return fullpath;
|
|
}
|
|
|
|
private uint get_basename_hash(bool cs) {
|
|
return cs ? str_hash(basename) : str_hash(basename.down());
|
|
}
|
|
|
|
/**
|
|
* Comparisons for Geary.FolderPath is defined as (a) empty paths are less-than non-empty paths
|
|
* and (b) each element is compared to the corresponding path element of the other FolderPath
|
|
* following collation rules for casefolded (case-insensitive) compared, and (c) shorter paths
|
|
* are less-than longer paths, assuming the path elements are equal up to the shorter path's
|
|
* length.
|
|
*/
|
|
public int compare_to(Geary.FolderPath other) {
|
|
if (this == other)
|
|
return 0;
|
|
|
|
// walk elements using as_list() as that includes the basename (whereas path does not),
|
|
// avoids the null problem, and makes comparisons straightforward
|
|
Gee.List<string> this_list = as_list();
|
|
Gee.List<string> other_list = other.as_list();
|
|
|
|
// if paths exist, do comparison of each parent in order
|
|
int min = int.min(this_list.size, other_list.size);
|
|
for (int ctr = 0; ctr < min; ctr++) {
|
|
int result = this_list[ctr].casefold().collate(other_list[ctr].casefold());
|
|
if (result != 0)
|
|
return result;
|
|
}
|
|
|
|
// paths up to the min element count are equal, shortest path is less-than, otherwise
|
|
// equal paths
|
|
return this_list.size - other_list.size;
|
|
}
|
|
|
|
public uint hash() {
|
|
if (stored_hash != uint.MAX)
|
|
return stored_hash;
|
|
|
|
bool cs = get_root().case_sensitive;
|
|
|
|
// always one element in path
|
|
uint calc = get_folder_at(0).get_basename_hash(cs);
|
|
|
|
int path_length = get_path_length();
|
|
for (int ctr = 1; ctr < path_length; ctr++)
|
|
calc ^= get_folder_at(ctr).get_basename_hash(cs);
|
|
|
|
stored_hash = calc;
|
|
|
|
return stored_hash;
|
|
}
|
|
|
|
private bool is_basename_equal(string cmp, bool cs) {
|
|
return cs ? (basename == cmp) : (basename.down() == cmp.down());
|
|
}
|
|
|
|
public bool equal_to(Geary.FolderPath other) {
|
|
int path_length = get_path_length();
|
|
if (other.get_path_length() != path_length)
|
|
return false;
|
|
|
|
bool cs = get_root().case_sensitive;
|
|
if (other.get_root().case_sensitive != cs) {
|
|
message("Comparing %s and %s with different case sensitivities", to_string(),
|
|
other.to_string());
|
|
}
|
|
|
|
for (int ctr = 0; ctr < path_length; ctr++) {
|
|
if (!get_folder_at(ctr).is_basename_equal(other.get_folder_at(ctr).basename, cs))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the fullpath using the default separator. Using only for debugging and logging.
|
|
*/
|
|
public string to_string() {
|
|
return get_fullpath();
|
|
}
|
|
}
|
|
|
|
public class Geary.FolderRoot : Geary.FolderPath {
|
|
public string? default_separator { get; private set; }
|
|
public bool case_sensitive { get; private set; }
|
|
|
|
public FolderRoot(string basename, string? default_separator, bool case_sensitive) {
|
|
base (basename);
|
|
|
|
this.default_separator = default_separator;
|
|
this.case_sensitive;
|
|
}
|
|
}
|
|
|