1 package org.daisy.pipeline.braille.common;
2
3 import java.io.InputStream;
4 import java.net.URL;
5 import java.util.HashMap;
6 import java.util.Iterator;
7 import java.util.Locale;
8 import java.util.Map;
9 import java.util.NoSuchElementException;
10 import java.util.Properties;
11
12 import com.google.common.base.Function;
13 import com.google.common.base.Optional;
14 import com.google.common.collect.ImmutableMap;
15 import static com.google.common.collect.Iterables.concat;
16 import static com.google.common.collect.Iterables.transform;
17
18 import org.daisy.pipeline.braille.common.util.Function2;
19 import org.daisy.pipeline.braille.common.util.Iterables;
20
21 public interface Provider<Q,X> {
22
23
24
25
26
27
28 public Iterable<X> get(Q query);
29
30
31
32
33
34 public static abstract class util {
35
36
37
38
39
40 public static <Q,X> Provider<Q,X> empty() {
41 return new Provider<Q,X>() {
42 public Iterable<X> get(Q query) {
43 return Optional.<X>absent().asSet();
44 }
45 };
46 }
47
48
49
50
51
52 public interface MemoizingProvider<Q,X> extends Memoizing<Q,Iterable<X>>, Provider<Q,X> {}
53
54
55
56
57 public static <Q,X> MemoizingProvider<Q,X> memoize(final Provider<Q,X> provider) {
58 return new Memoize<Q,X>() {
59 protected Iterable<X> _get(Q query) {
60 return Iterables.memoize(provider.get(query));
61 }
62 };
63 }
64
65 public static abstract class Memoize<Q,X> extends Memoizing.util.AbstractMemoizing<Q,Iterable<X>> implements MemoizingProvider<Q,X> {
66 protected Memoize() {
67 super();
68 }
69 protected Memoize(Memoize<Q,X> shareCacheWith) {
70 super(shareCacheWith);
71 }
72 protected abstract Iterable<X> _get(Q query);
73 public Iterable<X> get(Q query) {
74 return apply(query);
75 }
76 protected final Iterable<X> _apply(Q query) {
77 return _get(query);
78 }
79 }
80
81
82
83
84
85 public static abstract class SimpleMappingProvider<Q,X> implements Provider<Q,X> {
86 private Map<Q,String> map;
87 public abstract Q parseKey(String key);
88 public abstract X parseValue(String value);
89 public SimpleMappingProvider(URL properties) {
90 map = readProperties(properties);
91 }
92 public Iterable<X> get(Q query) {
93 String value = map.get(query);
94 if (value != null)
95 return Optional.fromNullable(parseValue(value)).asSet();
96 return Optional.<X>absent().asSet();
97 }
98 private Map<Q,String> readProperties(URL url) {
99 Map<Q,String> map = new HashMap<Q,String>();
100 try {
101 url.openConnection();
102 InputStream reader = url.openStream();
103 Properties properties = new Properties();
104 properties.loadFromXML(reader);
105 for (String key : properties.stringPropertyNames()) {
106 Q query = parseKey(key);
107 if (!map.containsKey(query))
108 map.put(query, properties.getProperty(key)); }
109 reader.close();
110 return new ImmutableMap.Builder<Q,String>().putAll(map).build(); }
111 catch (Exception e) {
112 throw new RuntimeException("Could not read properties file " + url, e); }
113 }
114 }
115
116
117
118
119
120 public static <Q,X> Provider<Q,X> dispatch(final Iterable<? extends Provider<Q,X>> dispatch) {
121 return new Dispatch<Q,X>() {
122 @SuppressWarnings(
123 "unchecked"
124 )
125 public Iterable<Provider<Q,X>> dispatch() {
126 return (Iterable<Provider<Q,X>>)dispatch;
127 }
128 };
129 }
130
131 public static abstract class Dispatch<Q,X> implements Provider<Q,X> {
132 public abstract Iterable<Provider<Q,X>> dispatch();
133 public Iterable<X> get(final Q query) {
134 return concat(transform(
135 dispatch(),
136 new Function<Provider<Q,X>,Iterable<X>>() {
137 public Iterable<X> apply(Provider<Q,X> provider) {
138 return provider.get(query); }}));
139 }
140 }
141
142
143
144
145
146 public static <X> Provider<Locale,X> varyLocale(final Provider<Locale,X> delegate) {
147 return new VaryLocale<Locale,X>() {
148 public Iterable<X> _get(Locale locale) {
149 return delegate.get(locale);
150 }
151 };
152 }
153
154 public static <Q,X> Provider<Q,X> varyLocale(final Provider<Q,X> delegate,
155 final Function<Q,Locale> getLocale,
156 final Function2<Q,Locale,Q> assocLocale) {
157 return new VaryLocale<Q,X>() {
158 public Iterable<? extends X> _get(Q query) {
159 return delegate.get(query);
160 }
161 public Locale getLocale(Q query) {
162 return getLocale.apply(query);
163 }
164 public Q assocLocale(Q query, Locale locale) {
165 return assocLocale.apply(query, locale);
166 }
167 };
168 }
169
170 public static abstract class VaryLocale<Q,X> implements Provider<Q,X> {
171 public abstract Iterable<? extends X> _get(Q query);
172 public abstract Locale getLocale(Q query);
173
174
175
176
177
178 public abstract Q assocLocale(Q query, Locale locale);
179 public Locale getLocale(Locale query) {
180 return query;
181 }
182 public Locale assocLocale(Locale query, Locale locale) {
183 return locale;
184 }
185 public final Iterable<X> get(final Q query) {
186 return new Iterable<X>() {
187 public Iterator<X> iterator() {
188 return new Iterator<X>() {
189 Iterator<? extends X> next = null;
190 int tryNext = 1;
191 Locale locale = getLocale(query);
192 public boolean hasNext() {
193 while (next == null || !next.hasNext()) {
194 switch (tryNext) {
195 case 1:
196 tryNext++;
197 next = _get(query).iterator();
198 if (locale == null || "".equals(locale.toString()))
199 tryNext = 4;
200 break;
201 case 2:
202 tryNext++;
203 if (!"".equals(locale.getVariant()))
204 next = _get(assocLocale(query, new Locale(locale.getLanguage(), locale.getCountry()))).iterator();
205 break;
206 case 3:
207 tryNext++;
208 if (!"".equals(locale.getCountry()))
209 next = _get(assocLocale(query, new Locale(locale.getLanguage()))).iterator();
210 break;
211 case 4:
212 tryNext++;
213 next = fallback(query).iterator();
214 break;
215 default:
216 return false; }}
217 return true;
218 }
219 public X next() {
220 if (!hasNext()) throw new NoSuchElementException();
221 return next.next();
222 }
223 public void remove() {
224 throw new UnsupportedOperationException();
225 }
226 };
227 }
228 };
229 }
230 public Iterable<X> fallback(Q query) {
231 return Optional.<X>absent().asSet();
232 }
233 }
234 }
235 }