1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.magicproject.database;
20
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.net.URL;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32
33 import net.sf.magicproject.clickable.targetable.card.CardModel;
34 import net.sf.magicproject.database.data.TranslatableData;
35 import net.sf.magicproject.database.propertyconfig.PropertyConfig;
36 import net.sf.magicproject.database.propertyconfig.PropertyProxyConfig;
37 import net.sf.magicproject.expression.IntValue;
38 import net.sf.magicproject.token.IdConst;
39 import net.sf.magicproject.tools.MToolKit;
40 import net.sf.magicproject.ui.i18n.LanguageManager;
41 import net.sf.magicproject.xml.XmlParser;
42 import net.sf.magicproject.xml.XmlParser.Node;
43
44 import org.apache.commons.lang.StringUtils;
45 import org.xml.sax.SAXException;
46
47 /***
48 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
49 * @author <a href="mailto:kismet-sl@users.sourceforge.net">Stefano "Kismet"
50 * Lenzi</a>
51 * @since 0.90
52 */
53 public class Proxy {
54
55 /***
56 * Private decalration of this proxy.
57 */
58 private final Map<String, Map<String, String>> aliases;
59
60 /***
61 * The stream base of stream urls.
62 */
63 private String streamBaseUrl;
64
65 /***
66 * The URL where data would be build from.
67 */
68 private List<UrlTokenizer> streams;
69
70 /***
71 * The language of this proxy.
72 */
73 private String language;
74
75 /***
76 * The encoding of stream retrieved from the proxy URL.
77 */
78 private String encoding;
79
80 /***
81 * The proxy's name as displayed in the GUI menus
82 */
83 private String name;
84
85 /***
86 * The proxy web site providing the data. Is only here for information
87 * purpose.
88 */
89 private String home;
90
91 /***
92 * The pictures configuration of this stream.
93 */
94 public List<PictureConfiguration> pictures = new ArrayList<PictureConfiguration>();
95
96 /***
97 * The properties managed by this proxy.
98 */
99 private List<PropertyConfig> properties;
100
101 /***
102 * The XML name of this proxy. The name corresponds to the XML file definition
103 * of this proxy
104 */
105 private String xmlName;
106
107 /***
108 * The proxy's name as displayed in the GUI menus
109 *
110 * @return The proxy's name as displayed in the GUI menus
111 */
112 public String getName() {
113 return name;
114 }
115
116 @Override
117 public String toString() {
118 return getName();
119 }
120
121 /***
122 * Return the XML name of this proxy. The name corresponds to the XML file
123 * definition of this proxy
124 *
125 * @return the XML name of this proxy.
126 */
127 public String getXmlName() {
128 return xmlName;
129 }
130
131 /***
132 * Create a new instance of this class.
133 *
134 * @param xmlFile
135 * the definition file of this proxy.
136 * @throws IOException
137 * If some other I/O error occurs
138 * @throws SAXException
139 * If some XML parse error occurs
140 */
141 public Proxy(File xmlFile) throws IOException, SAXException {
142 final XmlParser parser = new XmlParser();
143 final Node config = parser.parse(new FileInputStream(xmlFile));
144 xmlName = StringUtils.removeEnd(xmlFile.getName().toLowerCase(), ".xml");
145 name = config.getAttribute("name");
146 encoding = config.getAttribute("encoding");
147 language = config.getAttribute("language");
148 home = config.getAttribute("home");
149
150 final List<?> pictures = config.get("pictures").getNodes("picture");
151 for (int i = 0; i < pictures.size(); i++) {
152 final Node pictureStream = (Node) pictures.get(i);
153 this.pictures.add(new PictureConfiguration(
154 new UrlTokenizer(pictureStream), pictureStream.getAttribute("base")));
155 }
156
157
158 final Node aliases = config.get("alias");
159 this.aliases = new HashMap<String, Map<String, String>>();
160 if (aliases != null) {
161 List<Node> nodes = aliases.getNodes("alias");
162 for (int i = nodes.size(); i-- > 0;) {
163 final Node alias = nodes.get(i);
164 Map<String, String> nameSpace = this.aliases.get(alias
165 .getAttribute("property"));
166 if (nameSpace == null) {
167 nameSpace = new HashMap<String, String>();
168 this.aliases.put(alias.getAttribute("property"), nameSpace);
169 }
170 nameSpace.put(alias.getAttribute("local-value").toLowerCase(), alias
171 .getAttribute("ref"));
172 }
173 }
174
175
176 Node dataConfig = config.get("data");
177 Node streamConfig = dataConfig.get("streams");
178 if (streamConfig != null) {
179 streamBaseUrl = streamConfig.getAttribute("base");
180 final List<Node> streamsNode = streamConfig.getNodes("stream");
181 streams = new ArrayList<UrlTokenizer>(streamsNode.size());
182 for (int i = 0; i < streamsNode.size(); i++) {
183 streams.add(new UrlTokenizer(streamsNode.get(i)));
184 }
185 if (streams.isEmpty()) {
186 throw new RuntimeException(
187 "At least one stream configuration must be defined");
188 }
189
190
191 final List<Node> nodes = dataConfig.get("properties")
192 .getNodes("property");
193 properties = new ArrayList<PropertyConfig>(nodes.size());
194 for (int i = 0; i < nodes.size(); i++) {
195 properties.add(new PropertyProxyConfig(nodes.get(i)));
196 }
197 } else {
198 streams = new ArrayList<UrlTokenizer>(0);
199 properties = new ArrayList<PropertyConfig>(0);
200 }
201 }
202
203 /***
204 * @param cardModel
205 * the card model.
206 * @param constraints
207 * the constraints.
208 * @return the string read from one of the streams of this proxy.
209 * @throws IOException
210 * If some other I/O error occurs
211 */
212 private String getStringFromStream(CardModel cardModel,
213 Map<String, String> constraints) throws IOException {
214
215
216 int highestScore = -1;
217 UrlTokenizer stream = null;
218 for (int i = 0; i < streams.size(); i++) {
219 int score = streams.get(i).getUrlScore(constraints);
220 if (score > highestScore) {
221 highestScore = score;
222 stream = streams.get(i);
223 }
224 }
225
226
227 if (stream == null) {
228 return null;
229 }
230
231
232 final URL mainPage = new URL(streamBaseUrl
233 + stream.getUrl(cardModel, constraints, this));
234 final StringBuilder res = new StringBuilder(2000);
235 final InputStream proxyStream;
236 try {
237 proxyStream = MToolKit.getHttpConnection(mainPage).getInputStream();
238 } catch (Throwable e) {
239
240 throw new IOException(LanguageManager.getString("error.stream.null"));
241 }
242 if (proxyStream == null) {
243
244 throw new IOException(LanguageManager.getString("error.stream.null"));
245 }
246 final BufferedReader br = new BufferedReader(new InputStreamReader(
247 proxyStream, encoding));
248 String line = null;
249 while ((line = br.readLine()) != null) {
250 res.append(line);
251 }
252 return res.toString();
253 }
254
255 /***
256 * Create a new DatabaseCard from the given CardModel. The best stream is
257 * determinated depending the given constraints. The assocaited picture is
258 * also will be downloaded and loaded only during the first display of this
259 * picture.
260 *
261 * @param cardModel
262 * the object containing the card name.
263 * @param constraints
264 * set of constraints
265 * @return a new DatabaseCard from the given CardModel.
266 * @throws IOException
267 * If some other I/O error occurs
268 */
269 public DatabaseCard getDatabaseFromStream(CardModel cardModel,
270 Map<String, String> constraints) throws IOException {
271 final DatabaseCard databaseCard = new DatabaseCard(cardModel, this,
272 DatabaseFactory.pictureProxies);
273
274
275 final String stream = getStringFromStream(cardModel, constraints);
276
277
278 PropertyProxyConfig.values.clear();
279 PropertyProxyConfig.values.put("%last-offset", new IntValue(0));
280 for (PropertyConfig property : properties) {
281 final TranslatableData data = property.parseProperty(cardModel
282 .getCardName(), stream, this);
283 if (data != null) {
284 databaseCard.add(data);
285 }
286 }
287
288 return databaseCard;
289 }
290
291 /***
292 * Return the encoding of stream retrieved from the proxy URL.<br>
293 * Unreferenced method, but called with reflection.
294 *
295 * @return the encoding of stream retrieved from the proxy URL.
296 */
297 public String getEncoding() {
298 return encoding;
299 }
300
301 /***
302 * Return the proxy web site providing the data. Is only here for information
303 * purpose.<br>
304 * Unreferenced method, but called with reflection.
305 *
306 * @return the proxy web site providing the data.
307 */
308 public String getHome() {
309 return home;
310 }
311
312 /***
313 * Return the language of this proxy.<br>
314 * Unreferenced method, but called with reflection.
315 *
316 * @return the language of this proxy.
317 */
318 public String getLanguage() {
319 return language;
320 }
321
322 /***
323 * Return the referenced value of this proxy. If this proxy do not define the
324 * namaed alias, return the given <param>localValue</param>.
325 *
326 * @param nameSpace
327 * the name space (property)
328 * @param localValue
329 * the private-proxy value
330 * @return the referenced value of this proxy.
331 */
332 public String getGlobalValueFromLocal(String nameSpace, String localValue) {
333 if (localValue.length() > 400) {
334
335 return "invalid data";
336 }
337
338 final Map<String, String> alias = aliases.get(nameSpace);
339 if (alias != null && alias.containsKey(localValue.toLowerCase())) {
340 return alias.get(localValue.toLowerCase());
341 }
342 return localValue;
343 }
344
345 /***
346 * Return the referenced value of this proxy. If this proxy do not define the
347 * namaed alias, return the given <param>localValue</param>.
348 *
349 * @param nameSpace
350 * @param localValue
351 * @return the referenced value of this proxy.
352 */
353 public String getLocalValueFromGlobal(String nameSpace, String localValue) {
354 final Map<String, String> alias = aliases.get(nameSpace);
355 if (alias != null && alias.containsValue(localValue.toLowerCase())) {
356 for (String key : alias.keySet()) {
357 if (alias.get(key).equals(localValue.toLowerCase())) {
358 return key;
359 }
360 }
361 }
362 return localValue;
363 }
364
365 /***
366 * Return a list of remote picture paths considering proxy, properties and the
367 * given card. The returned order corresponds to the priority.
368 *
369 * @param cardModel
370 * the card model.
371 * @param data
372 * the translated data.
373 * @return a list of remote picture paths.
374 */
375 public List<String> getRemotePictures(CardModel cardModel,
376 Map<String, TranslatableData> data) {
377 List<String> res = new ArrayList<String>(pictures.size());
378 for (PictureConfiguration p : pictures) {
379 try {
380 final String localUrl = p.getPictureUrl().getUrl(cardModel, data, this);
381 res.add(p.getProxyBaseUrl() + "/" + localUrl);
382 } catch (Exception e) {
383
384 res.add(null);
385 }
386 }
387 return res;
388 }
389
390 /***
391 * Return a list of local picture paths considering proxy, properties and the
392 * given card. The returned order corresponds to the priority.
393 *
394 * @param cardModel
395 * the card model.
396 * @param data
397 * the translated data.
398 * @return a list of local picture paths.
399 */
400 public List<String> getLocalPictures(CardModel cardModel,
401 Map<String, TranslatableData> data) {
402 List<String> res = new ArrayList<String>(pictures.size());
403 for (PictureConfiguration p : pictures) {
404 try {
405 final String localUrl = p.getPictureUrl().getUrl(cardModel, data, this);
406 res.add(MToolKit.getTbsPicture(IdConst.PROXIES_LOCATION + "/"
407 + getValidPath(getXmlName()) + "/" + localUrl, false));
408 } catch (Exception e) {
409
410 res.add(null);
411 }
412
413 }
414 return res;
415 }
416
417 /***
418 * Simple method returning a suitable path for Windows, Linux,...
419 *
420 * @param path
421 * any non null string
422 * @return a suitable path for Windows, Linux,...
423 */
424 private String getValidPath(String path) {
425 return path.replace('/', '_').replace(':', '_').replace('*', '_').replace(
426 '//', '_').replace('%', '_').replace('"', '_').replace('\'', '_')
427 .replace('&', '_').replace('$', '_').replace('~', '_');
428 }
429
430 }