package org.tessell.generators.views;
import static org.apache.commons.lang.StringUtils.join;
import static org.apache.commons.lang.StringUtils.splitPreserveAllTokens;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import joist.util.Join;
import org.apache.commons.io.FileUtils;
/**
* Caches the "ui:with" types of {@code ui.xml} files between {@code ViewGenerator}
* runs so that we don't have to re-parse each file every time.
*/
public class UiXmlCache {
private static final int cacheVersion = 10;
private static final String viewgenTimestampKey = "viewgenTimestamp";
private final Map<String, Entry> entries = new HashMap<String, Entry>();
private String viewgenTimestamp;
public static UiXmlCache loadOrCreate(final File outputDirectory) {
UiXmlCache c = new UiXmlCache();
if (cache(outputDirectory).exists()) {
try {
for (String line : FileUtils.readLines(cache(outputDirectory))) {
if (line.startsWith(viewgenTimestampKey)) {
c.viewgenTimestamp = line.split("=")[1];
} else {
Entry e = new Entry(line);
c.entries.put(e.uiXmlFileName, e);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return c;
}
private static File cache(File outputDirectory) {
return new File(outputDirectory, "./.viewGenerator." + cacheVersion + ".cache");
}
private UiXmlCache() {
}
/** @return whether we have an entry for {@code uiXmlFile} */
public boolean has(UiXmlFile uiXml) {
return entries.keySet().contains(uiXml.getPath());
}
/** Updates the {@code uiXmlFile} entry with {@code withTypes}. */
public void update(UiXmlFile uiXml) {
entries.put(uiXml.getPath(), new Entry(//
uiXml.getPath(),
uiXml.getFreshWiths(),
uiXml.getFreshStyles(),
uiXml.getStubDependencies()));
}
/** @return the {@code ui:with} declarations for {@code uiXml}. */
public List<UiWithDeclaration> getCachedWiths(UiXmlFile uiXml) {
return entries.get(uiXml.getPath()).withs;
}
/** @return the {@code ui:style} declarations for {@code uiXml}. */
public List<UiStyleDeclaration> getCachedStyles(UiXmlFile uiXml) {
return entries.get(uiXml.getPath()).styles;
}
/** @return the stub dependencies for {@code uiXml}. */
public List<String> getCachedStubDependencies(UiXmlFile uiXml) {
return entries.get(uiXml.getPath()).stubDependencies;
}
/** Saves the cache to the file system for loading next time. */
public void save(File outputDirectory, String viewgenTimestamp) {
try {
List<String> lines = entriesToLines();
lines.add(viewgenTimestampKey + "=" + viewgenTimestamp);
FileUtils.writeLines(cache(outputDirectory), lines);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public String getViewgenTimestamp() {
return viewgenTimestamp;
}
/** @return the cache entries as a list of lines. */
private List<String> entriesToLines() {
List<String> lines = new ArrayList<String>();
for (Entry e : entries.values()) {
lines.add(e.toLine());
}
return lines;
}
/** One entry per ui.xml file. */
private static class Entry {
// each entry is serialized to 1 line in the cache file, comma separated into a String[]
private final String uiXmlFileName;
private final ArrayList<UiWithDeclaration> withs = new ArrayList<UiWithDeclaration>();
private final ArrayList<UiStyleDeclaration> styles = new ArrayList<UiStyleDeclaration>();
private final ArrayList<String> stubDependencies = new ArrayList<String>();
/** Make a new entry from the cached line. */
private Entry(String line) {
final String[] parts = splitPreserveAllTokens(line, ";");
uiXmlFileName = parts[0];
// parse out ui:withs
if (parts[1].length() > 0) {
for (String with : parts[1].split(",")) {
String[] wp = with.split(" ");
withs.add(new UiWithDeclaration(wp[0], wp[1]));
}
}
// parse out ui:styles
if (parts[2].length() > 0) {
for (String style : parts[2].split(",")) {
String[] sp = style.split(" ");
styles.add(new UiStyleDeclaration(sp[0], sp[1]));
}
}
// parse out stub dependencies
if (parts[3].length() > 0) {
stubDependencies.addAll(Arrays.asList(parts[3]));
}
}
/** Make a new entry from a new {@code ui.xml} file with its {@code ui:with} types. */
private Entry(String uiXmlFileName, List<UiWithDeclaration> withs, List<UiStyleDeclaration> styles, List<String> stubDependencies) {
this.uiXmlFileName = uiXmlFileName;
this.withs.addAll(withs);
this.styles.addAll(styles);
this.stubDependencies.addAll(stubDependencies);
}
private String toLine() {
final List<String> withStrings = new ArrayList<String>();
for (final UiWithDeclaration with : withs) {
withStrings.add(with.type + " " + with.name);
}
final List<String> styleStrings = new ArrayList<String>();
for (final UiStyleDeclaration style : styles) {
styleStrings.add(style.type + " " + style.name);
}
final String p0 = uiXmlFileName;
final String p1 = Join.comma(withStrings);
final String p2 = Join.comma(styleStrings);
final String p3 = Join.comma(stubDependencies);
return join(new String[] { p0, p1, p2, p3 }, ";");
}
}
}