001package net.filebot.format; 002 003import static java.util.Collections.*; 004import static net.filebot.media.XattrMetaInfo.*; 005import static net.filebot.util.FileUtilities.*; 006import static net.filebot.util.RegularExpressions.*; 007 008import java.io.File; 009import java.util.AbstractList; 010import java.util.Collection; 011import java.util.HashSet; 012import java.util.LinkedHashMap; 013import java.util.List; 014import java.util.Map; 015import java.util.RandomAccess; 016import java.util.Set; 017import java.util.regex.Pattern; 018 019import groovy.json.JsonSlurper; 020import groovy.xml.XmlSlurper; 021 022import net.filebot.MemoryCache; 023import net.filebot.Resource; 024 025public class DataFile { 026 027 private final File file; 028 private final boolean directory; 029 private final long lastModified; 030 031 public DataFile(File file) { 032 this.file = file; 033 this.directory = file.isDirectory(); 034 this.lastModified = file.lastModified(); 035 } 036 037 public boolean isStale() { 038 return this.lastModified != file.lastModified(); 039 } 040 041 private Map<Object, Object> csv() throws Exception { 042 Map<Object, Object> value = new LinkedHashMap<Object, Object>(); 043 044 if (directory) { 045 // read xattr file structure 046 for (File f : listFiles(file, NOT_HIDDEN)) { 047 Object m = xattr.getMetaInfo(f); 048 if (m != null) { 049 value.put(f, m); 050 } 051 } 052 } else { 053 // read CSV file 054 Pattern[] delimiter = { TAB, SEMICOLON }; 055 for (String line : readLines(file)) { 056 for (Pattern d : delimiter) { 057 String[] field = d.split(line, 2); 058 if (field.length >= 2) { 059 value.put(field[0].trim(), field[1].trim()); 060 break; 061 } 062 } 063 } 064 } 065 return unmodifiableMap(value); 066 } 067 068 private List<String> lines() throws Exception { 069 if (directory) { 070 return new LookupList(file.list()); 071 } 072 return new LookupList(readLines(file)); 073 } 074 075 private Object xml() throws Exception { 076 return new XmlSlurper().parse(file); 077 } 078 079 private Object json() throws Exception { 080 return new JsonSlurper().parse(file); 081 } 082 083 public final Resource<Map<Object, Object>> csv = Resource.lazy(this::csv); 084 public final Resource<List<String>> lines = Resource.lazy(this::lines); 085 public final Resource<Object> xml = Resource.lazy(this::xml); 086 public final Resource<Object> json = Resource.lazy(this::json); 087 088 public static synchronized DataFile getDataFile(File file) throws Exception { 089 DataFile data = cache.getIfPresent(file); 090 if (data == null || data.isStale()) { 091 data = new DataFile(file); 092 cache.put(file, data); 093 } 094 return data; 095 } 096 097 private static final MemoryCache<File, DataFile> cache = MemoryCache.forMinutes(); 098 099} 100 101class LookupList extends AbstractList<String> implements RandomAccess { 102 103 private final String[] values; 104 105 public LookupList(Collection<String> values) { 106 this.values = values.toArray(new String[0]); 107 } 108 109 public LookupList(String[] values) { 110 this.values = values; 111 } 112 113 @Override 114 public String get(int index) { 115 return values[index]; 116 } 117 118 @Override 119 public int size() { 120 return values.length; 121 } 122 123 private Set<String> lookup; 124 125 private Set<String> getLookup() { 126 if (lookup == null) { 127 lookup = new HashSet<String>(this); 128 } 129 return lookup; 130 } 131 132 @Override 133 public boolean contains(Object object) { 134 if (object == null) { 135 return false; 136 } 137 return getLookup().contains(object.toString()); 138 } 139}