1 | /* This file is part of Aard Dictionary for Android <http://aarddict.org>. |
2 | * |
3 | * This program is free software: you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License version 3 |
5 | * as published by the Free Software Foundation. |
6 | * |
7 | * This program is distributed in the hope that it will be useful, |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | * GNU General Public License <http://www.gnu.org/licenses/gpl-3.0.txt> |
11 | * for more details. |
12 | * |
13 | * Copyright (C) 2010 Igor Tkach |
14 | */ |
15 | |
16 | package aarddict; |
17 | |
18 | import java.io.IOException; |
19 | import java.util.ArrayList; |
20 | import java.util.Collections; |
21 | import java.util.Comparator; |
22 | import java.util.HashSet; |
23 | import java.util.Iterator; |
24 | import java.util.List; |
25 | import java.util.Map; |
26 | import java.util.Set; |
27 | import java.util.UUID; |
28 | |
29 | import android.util.Log; |
30 | |
31 | public final class Library extends ArrayList<Volume> { |
32 | |
33 | int maxRedirectLevels = 5; |
34 | |
35 | private final static String TAG = Library.class.getName(); |
36 | |
37 | public Iterator<Entry> followLink(final String word, String fromVolumeId) throws ArticleNotFound { |
38 | Log.d(TAG, String.format("Follow link \"%s\", %s", word, |
39 | fromVolumeId)); |
40 | Volume fromDict = getVolume(fromVolumeId); |
41 | Metadata fromMeta = fromDict.metadata; |
42 | |
43 | LookupWord lookupWord = LookupWord.splitWord(word); |
44 | Log.d(TAG, lookupWord.toString()); |
45 | String nameSpace = lookupWord.nameSpace; |
46 | |
47 | Log.d(TAG, String.format("Name space: %s", nameSpace)); |
48 | Map<String, String> interwikiMap = fromMeta.getInterwikiMap(); |
49 | String nsServerUrl = interwikiMap.get(nameSpace); |
50 | List<UUID> matchingDicts = findMatchingDicts(nsServerUrl); |
51 | if (matchingDicts.isEmpty()) |
52 | matchingDicts.add(fromDict.getDictionaryId()); |
53 | |
54 | if (nsServerUrl == null) { |
55 | //namespace did not resolve into server url, |
56 | //maybe it's not a name space, just article title with ":" in it |
57 | lookupWord.mergeNameSpace(); |
58 | } |
59 | |
60 | Comparator<Entry>[] comparators = EntryComparators.ALL_FULL; |
61 | |
62 | if (lookupWord.word != null) { |
63 | if (lookupWord.word.length() == 1) |
64 | comparators = EntryComparators.EXACT; |
65 | else |
66 | if (lookupWord.word.length() == 2) |
67 | comparators = EntryComparators.EXACT_IGNORE_CASE; |
68 | } |
69 | |
70 | final List<Volume> dicts = new ArrayList<Volume>(this); |
71 | for (int i = 0; i < matchingDicts.size(); i++) { |
72 | UUID target = matchingDicts.get(i); |
73 | Comparator<Volume> c = new PreferredDictionaryComparator(target); |
74 | Collections.sort(dicts.subList(i, dicts.size()), c); |
75 | } |
76 | |
77 | MatchIterator result = new MatchIterator(dicts, comparators, lookupWord); |
78 | if (result.hasNext()) { |
79 | return result; |
80 | } |
81 | else { |
82 | throw new ArticleNotFound(lookupWord); |
83 | } |
84 | } |
85 | |
86 | private List<UUID> findMatchingDicts(String serverUrl) { |
87 | Log.d(TAG, "Looking for dictionary with server url " |
88 | + serverUrl); |
89 | Set<UUID> seen = new HashSet<UUID>(); |
90 | List<UUID> result = new ArrayList<UUID>(); |
91 | if (serverUrl == null) { |
92 | Log.d(TAG, "Server url is null"); |
93 | return result; |
94 | } |
95 | for (Volume d : this) { |
96 | String articleURLTemplate = d.getArticleURLTemplate(); |
97 | Log.d(TAG, "Looking at article url template: " |
98 | + articleURLTemplate); |
99 | if (articleURLTemplate != null |
100 | && serverUrl.equals(articleURLTemplate)) { |
101 | Log.d(TAG, String.format( |
102 | "Dictionary with server url %s found: %s", serverUrl, d |
103 | .getDictionaryId())); |
104 | if (!seen.contains(d.getDictionaryId())) |
105 | result.add(d.getDictionaryId()); |
106 | } |
107 | } |
108 | if (result.isEmpty()) { |
109 | Log.d(TAG, String.format( |
110 | "Dictionary with server url %s not found", serverUrl)); |
111 | } |
112 | return result; |
113 | } |
114 | |
115 | public Iterator<Entry> bestMatch(String word) { |
116 | LookupWord lookupWord = LookupWord.splitWord(word); |
117 | //best match is used with human input, |
118 | //assume ":" is never used as namespace separator |
119 | lookupWord.mergeNameSpace(); |
120 | return new MatchIterator(EntryComparators.ALL, this, lookupWord); |
121 | } |
122 | |
123 | public Article getArticle(Entry e) throws IOException { |
124 | Volume d = getVolume(e.volumeId); |
125 | Article a = d.readArticle(e.articlePointer); |
126 | a.title = e.title; |
127 | a.section = e.section; |
128 | return a; |
129 | } |
130 | |
131 | Article redirect(Article article, int level) |
132 | throws RedirectTooManyLevels, ArticleNotFound, IOException { |
133 | if (level > maxRedirectLevels) { |
134 | throw new RedirectTooManyLevels(); |
135 | } |
136 | |
137 | if (!article.isRedirect()) { |
138 | return article; |
139 | } |
140 | |
141 | Iterator<Entry> result = followLink(article.getRedirect(), article.volumeId); |
142 | Entry redirectEntry = result.next(); |
143 | Article redirectArticle = getArticle(redirectEntry); |
144 | return redirect(redirectArticle, level + 1); |
145 | } |
146 | |
147 | public Article redirect(Article article) |
148 | throws RedirectTooManyLevels, ArticleNotFound, IOException { |
149 | Article result = redirect(article, 0); |
150 | if (result != article) { |
151 | result.redirectedFromTitle = article.title; |
152 | } |
153 | return result; |
154 | } |
155 | |
156 | public Volume getVolume(String volumeId) { |
157 | |
158 | for (Volume d : this) { |
159 | if (d.sha1sum.equals(volumeId)) { |
160 | return d; |
161 | } |
162 | } |
163 | return null; |
164 | } |
165 | |
166 | public void makeFirst(String volumeId) { |
167 | Volume d = getVolume(volumeId); |
168 | if (d != null) { |
169 | Comparator<Volume> c = new PreferredDictionaryComparator(d |
170 | .getDictionaryId()); |
171 | Collections.sort(this, c); |
172 | } |
173 | } |
174 | } |