package com.hdweiss.morgand.data.dao;
import android.text.TextUtils;
import com.hdweiss.morgand.data.OrgCalendarEntry;
import com.hdweiss.morgand.data.OrgNodeUtils;
import com.hdweiss.morgand.settings.PreferenceUtils;
import com.j256.ormlite.dao.ForeignCollection;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.stmt.Where;
import com.j256.ormlite.table.DatabaseTable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@DatabaseTable(tableName = "OrgNodes")
public class OrgNode {
public enum Type {
File, Directory, Date, Headline, Body, Drawer, Checkbox, Setting, Table
}
public enum State {
Clean, Updated, Added, Deleted
}
@DatabaseField(generatedId = true)
public int Id;
public static final String PARENT_FIELD_NAME = "parent";
@DatabaseField(foreign = true, columnName = PARENT_FIELD_NAME, foreignAutoRefresh=true, canBeNull=true, maxForeignAutoRefreshLevel=3)
public OrgNode parent;
@ForeignCollectionField(eager = false, foreignFieldName = PARENT_FIELD_NAME)
public ForeignCollection<OrgNode> children;
@DatabaseField
public Type type;
public static final String LEVEL_FIELD_NAME = "level";
@DatabaseField(columnName = LEVEL_FIELD_NAME)
public int level = 0;
public static final String LINENUMBER_FIELD_NAME = "lineNumber";
@DatabaseField(columnName = LINENUMBER_FIELD_NAME)
public int lineNumber = -1;
public static final String STATE_FIELD_NAME = "state";
@DatabaseField(columnName = STATE_FIELD_NAME)
public State state = State.Clean;
public static final String FILE_FIELD_NAME = "file";
@DatabaseField(foreign = true, columnName = FILE_FIELD_NAME)
public OrgFile file;
public static final String TITLE_FIELD_NAME = "title";
@DatabaseField(columnName = TITLE_FIELD_NAME)
public String title = "";
@DatabaseField
public String tags = "";
@DatabaseField
public String inheritedTags = "";
public String toString() {
if (type != Type.Headline)
return title;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < level; i++)
builder.append("*");
builder.append(" ");
if (TextUtils.isEmpty(title) == false) {
builder.append(title.trim());
builder.append("\t" + tags);
}
return builder.toString();
}
public String toStringRecursively() {
StringBuilder builder = new StringBuilder(toString());
for(OrgNode child : children)
builder.append("\n").append(child.toStringRecursively());
return builder.toString();
}
public boolean isEditable() {
if (type == Type.File || type == Type.Directory)
return false;
if (file.isEditable() == false)
return false;
return true;
}
public String getProperty(String propertyName) {
if (type == Type.Headline) {
Pattern propertyPattern = Pattern.compile(":" + propertyName + ":\\s+(.+)");
for(OrgNode child: children) {
if (child.type == Type.Drawer) {
Matcher matcher = propertyPattern.matcher(child.title);
if (matcher.find()) {
return matcher.group(1).trim();
}
}
}
return null;
}
if (parent != null)
return parent.getProperty(propertyName);
return null;
}
public String getTitle() {
if (type != Type.Headline) {
if (parent != null) {
return parent.getTitle();
}
else {
return "";
}
}
return title;
}
public String getTodo() {
if (type == Type.Headline) {
Matcher matcher = OrgNodeUtils.headingPattern.matcher(title);
if (matcher.find())
return matcher.group(1);
}
return "";
}
public String getBody() {
if (type == Type.Headline) {
StringBuilder body = new StringBuilder();
for(OrgNode child: children) {
if (child.type == Type.Body)
body.append(child.title);
}
return body.toString();
} else if (parent != null)
return parent.getBody();
return title;
}
public ArrayList<OrgNode> getDisplayChildren() {
ArrayList<OrgNode> nodes = new ArrayList<OrgNode>();
boolean showSettings = PreferenceUtils.showSettings();
boolean showDrawers = PreferenceUtils.showDrawers();
for(OrgNode node: children) {
if (node.state == State.Deleted)
continue;
switch (node.type) {
case Drawer:
if (showDrawers)
nodes.add(node);
break;
case Setting:
if (showSettings)
nodes.add(node);
break;
default:
nodes.add(node);
}
}
return nodes;
}
public OrgNode addChild(Type type) {
OrgNode node = new OrgNode();
node.parent = this;
node.file = this.file;
node.type = type;
node.level = this.level + 1;
node.inheritedTags = OrgNodeUtils.combineTags(tags, inheritedTags, PreferenceUtils.getExcludedTags());
return node;
}
public OrgNode addChild(Type type, String title) {
OrgNode node = addChild(type);
node.title = title;
OrgNodeRepository.create(node);
return node;
}
public int getNextNodeLineNumber() {
try {
Where<OrgNode, Integer> where = OrgNodeRepository.queryBuilder().orderBy(LINENUMBER_FIELD_NAME, true).where();
where.eq(FILE_FIELD_NAME, file).and().gt(LINENUMBER_FIELD_NAME, lineNumber);
OrgNode node = where.queryForFirst();
if (node != null)
return node.lineNumber;
else
return Integer.MAX_VALUE; // Current node seems to be last in file
} catch (SQLException e) {
e.printStackTrace();
}
return -1;
}
/**
* @return line number of next node on same or higher level than current node.
*/
public int getSiblingLineNumber() {
try {
Where<OrgNode, Integer> where = OrgNodeRepository.queryBuilder().orderBy(LINENUMBER_FIELD_NAME, true).where();
where.eq(FILE_FIELD_NAME, file).and().gt(LINENUMBER_FIELD_NAME, lineNumber).and().le(LEVEL_FIELD_NAME, level);
OrgNode node = where.queryForFirst();
if (node != null) {
if (node.type != Type.File)
return node.lineNumber;
}
else
return Integer.MAX_VALUE; // Current node seems to be last in file
} catch (SQLException e) {
e.printStackTrace();
}
return -1;
}
public boolean isNodeWritable() {
if (parent != null) {
if (parent.state == State.Added)
return false;
if (parent.state == State.Deleted)
return false;
return parent.isNodeWritable();
}
return true;
}
public List<OrgCalendarEntry> getOrgNodeDates() {
ArrayList<OrgCalendarEntry> dates = new ArrayList<OrgCalendarEntry>();
Matcher matcher = OrgNodeUtils.dateMatcher.matcher(title);
while(matcher.find()) {
String type = matcher.group(1);
String startDate = matcher.group(2);
String endDate = matcher.group(3); // TODO Support day ranges
try {
OrgCalendarEntry orgCalendarEntry = new OrgCalendarEntry(startDate);
orgCalendarEntry.type = type != null ? type : "";
orgCalendarEntry.setTitle(getTitle());
dates.add(orgCalendarEntry);
} catch (IllegalArgumentException ex) {}
}
return dates;
}
public static class OrgNodeCompare implements Comparator<OrgNode> {
@Override
public int compare(OrgNode node1, OrgNode node2) {
return node1.title.compareTo(node2.title);
}
}
}