package folioxml.export.structure;
import folioxml.core.InvalidMarkupException;
import folioxml.export.FileNode;
import folioxml.export.StaticFileNode;
import folioxml.xml.XmlRecord;
import java.util.*;
public class IdSlugProvider extends BaseFileSplitter {
private StaticFileNode silentRoot = new StaticFileNode(null);
public IdSlugProvider(String levelRegex, String splitOnFieldName) {
super(levelRegex, splitOnFieldName);
}
public IdSlugProvider(String levelRegex, String splitOnFieldName, Integer idKind, Integer root_index, Integer start_index) {
super(levelRegex, splitOnFieldName);
this.idKind = idKind == null ? 0 : idKind;
this.start_index = start_index == null ? 1 : start_index;
this.root_index = root_index == null ? 1 : root_index;
this.sequentialIndex = root_index;
}
int idKind;
int start_index;
int root_index;
public IdSlugProvider() {
}
public IdSlugProvider(String levelRegex) {
super(levelRegex);
}
int sequentialIndex;
@Override
public String getRelativePathFor(FileNode fn) {
return getRelativePathFor(fn, idKind);
//TODO - verify 1-1 mapping between all relative paths and all file node values.
}
public String getRelativePathFor(FileNode fn, int kind) {
if (kind == 0) {
return getSlugPathFor(fn);
} else if (kind == 1) {
return Integer.toString((Integer) fn.getBag().get("global-index"));
} else if (kind == 2) {
return getNestedIntegerPathFor(fn);
} else if (kind == 3) {
return ((UUID) fn.getBag().get("guid")).toString();
} else if (kind == 4) {
if (fn.getBag().get("folio-id") == null) {
return (fn.getBag().get("infobase-id") + "-i" + Integer.toString((Integer) fn.getBag().get("global-index"))).toLowerCase();
} else {
return (fn.getBag().get("infobase-id") + "-" + (String) fn.getBag().get("folio-id")).toLowerCase();
}
} else if (kind > 4) {
//Try to use split field value as ID, then fall back to kind - 5
Object otext = fn.getBag().get("split-field-text");
if (otext == null)
return getRelativePathFor(fn, kind - 5);
else
return (String) otext;
}
return getSlugPathFor(fn);
}
public String getNestedIntegerPathFor(FileNode fn) {
StringBuilder sb = new StringBuilder();
Deque<StaticFileNode> list = ((StaticFileNode) fn).getAncestors(true);
StaticFileNode n = null;
while (!list.isEmpty()) {
sb.append((String) list.removeLast().getBag().get("local-index"));
if (!list.isEmpty()) sb.append('.');
}
return sb.toString();
}
public String getSlugPathFor(FileNode fn) {
StringBuilder sb = new StringBuilder();
Deque<StaticFileNode> list = ((StaticFileNode) fn).getAncestors(true);
StaticFileNode n = null;
while (!list.isEmpty()) {
sb.append((String) list.removeLast().getBag().get("slug"));
if (!list.isEmpty()) sb.append('/');
}
return sb.toString();
}
public void PopulateNodeInfo(XmlRecord r, FileNode f) throws InvalidMarkupException {
//Infobase ID comes in handy when generating the slug
XmlRecord root = r.getRoot();
if (root != null && root.get("infobaseId") != null) {
f.getBag().put("infobase-id", root.get("infobaseId"));
}
String[] levels = root.get("levelDefOrder").split(",");
String level = r.getLevelType();
if (level != null) {
f.getBag().put("folio-level", level);
for (int i = 0; i < levels.length; i++) {
if (levels[i].equalsIgnoreCase(level)) {
f.getBag().put("folio-level-index", i + 1);
f.getAttributes().put("level-index", Integer.toString(i + 1));
break;
}
}
}
f.getBag().put("slug", getSlug(r, f));
f.getBag().put("guid", UUID.randomUUID());
FileNode parentScope = f.getParent() == null ? silentRoot : f.getParent();
f.getBag().put("local-index", incrementChildCount(parentScope));
f.getBag().put("global-index", sequentialIndex);
sequentialIndex++;
f.getBag().put("folio-id", r.get("folioId")); //TODO: verify uniqueness
if (r.get("heading") != null && r.get("heading").length() > 0) {
f.getAttributes().put("heading", r.get("heading"));
}
if (r.get("headingOnly") != null && r.get("headingOnly").length() > 0) {
f.getAttributes().put("headingOnly", r.get("headingOnly"));
}
String splitText = getSplitFieldText(r);
if (splitText != null && splitText.length() > 0) {
//ONLY ADD IF unique across infobase
Integer totalCount = incrementValueCount(splitText, "splitTexts", silentRoot);
if (totalCount > 1) {
System.out.append("Skipping use of split-text-field for ID - value '" + splitText + "' has been used before and is not unique.\n");
} else {
f.getBag().put("split-field-text", splitText);
f.getAttributes().put(splitOnFieldName.toLowerCase() + "-text", splitText);
}
}
populateHeadings(f, f);
}
private void populateHeadings(FileNode from, FileNode to) {
Object headingIndex = from.getBag().get("folio-level-index");
if (headingIndex != null) {
String heading = from.getAttributes().get("heading");
if (heading != null && heading.length() > 0) {
to.getAttributes().put("heading" + headingIndex, heading);
}
}
if (from.getParent() != null)
populateHeadings(from.getParent(), to);
}
private String getSlug(XmlRecord r, FileNode f) throws InvalidMarkupException {
String heading = r.get("heading");
String slug = heading == null ? null : slugify(heading, 100);
if (r.isRootRecord() && (slug == null || slug.isEmpty())) {
Object name = f.getBag().get("infobase-id");
if (name == null) name = "index";
slug = (String) name;
}
if (slug == null) slug = "untitled";
FileNode parentScope = f.getParent() == null ? silentRoot : f.getParent();
Integer suffix = incrementSlug(slug, parentScope);
if (suffix > 1) return slug + "-" + suffix;
else return slug;
}
protected String slugify(String text, int maxLength) {
String slug = text.toLowerCase(Locale.ENGLISH).replaceAll("[^a-zA-Z0-9-_~$]", " ").trim();
slug = slug.replaceAll("[ \t\r\n]+", "-").toLowerCase(Locale.ENGLISH);
if (slug.length() > maxLength) slug = slug.substring(0, maxLength);
return slug;
}
protected Integer incrementSlug(String slug, FileNode scope) {
return incrementValueCount(slug, "childSlugs", scope);
}
protected Integer incrementValueCount(String value, String dictName, FileNode scope) {
//Access sibling values to ensure uniqueness.
Object oslugs = scope.getBag().get(dictName);
if (oslugs == null) {
oslugs = new HashMap<String, Integer>();
scope.getBag().put(dictName, oslugs);
}
Map<String, Integer> allValues = (Map<String, Integer>) oslugs;
String normalizedValue = value.trim().toLowerCase();
if (allValues.get(normalizedValue) == null) {
allValues.put(normalizedValue, 0);
}
//Increment
allValues.put(normalizedValue, allValues.get(normalizedValue) + 1);
return allValues.get(normalizedValue);
}
protected Integer incrementChildCount(FileNode scope) {
//Access sibling slugs to ensure uniqueness.
Object count = scope.getBag().get("childCount");
if (count == null) {
Integer index_at = ((scope == silentRoot) ? root_index : start_index);
scope.getBag().put("childCount", index_at - 1);
}
Integer child_count = (Integer) scope.getBag().get("childCount");
child_count++;
scope.getBag().put("childCount", child_count);
return child_count;
}
}