eebbe09849520b9b681639132e24d7a946425797
[fanfix.git] / src / be / nikiroo / fanfix / searchable / SearchableTag.java
1 package be.nikiroo.fanfix.searchable;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * This class represents a tag that can be searched on a supported website.
8 *
9 * @author niki
10 */
11 public class SearchableTag {
12 private String id;
13 private String name;
14 private boolean complete;
15 private long count;
16
17 private SearchableTag parent;
18 private List<SearchableTag> children;
19
20 /**
21 * The number of stories result pages this tag can get.
22 * <p>
23 * We keep more information than what the getter/setter returns/accepts.
24 * <ul>
25 * <li>-2: this tag does not support stories results (not a leaf tag)</li>
26 * <li>-1: the number is not yet known, but will be known after a
27 * {@link BasicSearchable#fillTag(SearchableTag)} operation</li>
28 * <li>X: the number of pages</li>
29 * </ul>
30 */
31 private int pages;
32
33 /**
34 * Create a new {@link SearchableTag}.
35 * <p>
36 * Note that tags are complete by default.
37 *
38 * @param id
39 * the ID (usually a way to find the linked stories later on)
40 * @param name
41 * the tag name, which can be displayed to the user
42 * @param leaf
43 * the tag is a leaf tag, that is, it will not return subtags
44 * with {@link BasicSearchable#fillTag(SearchableTag)} but will
45 * return stories with
46 * {@link BasicSearchable#search(SearchableTag)}
47 */
48 public SearchableTag(String id, String name, boolean leaf) {
49 this(id, name, leaf, true);
50 }
51
52 /**
53 * Create a new {@link SearchableTag}.
54 *
55 * @param id
56 * the ID (usually a way to find the linked stories later on)
57 * @param name
58 * the tag name, which can be displayed to the user
59 * @param leaf
60 * the tag is a leaf tag, that is, it will not return subtags
61 * with {@link BasicSearchable#fillTag(SearchableTag)} but will
62 * return stories with
63 * {@link BasicSearchable#search(SearchableTag)}
64 * @param complete
65 * the tag {@link SearchableTag#isComplete()} or not
66 */
67 public SearchableTag(String id, String name, boolean leaf, boolean complete) {
68 this.id = id;
69 this.name = name;
70 this.complete = complete;
71
72 setLeaf(leaf);
73
74 children = new ArrayList<SearchableTag>();
75 }
76
77 /**
78 * The ID (usually a way to find the linked stories later on).
79 *
80 * @return the ID
81 */
82 public String getId() {
83 return id;
84 }
85
86 /**
87 * The tag name, which can be displayed to the user.
88 *
89 * @return then name
90 */
91 public String getName() {
92 return name;
93 }
94
95 /**
96 * Non-complete, non-leaf tags can still be completed via a
97 * {@link BasicSearchable#fillTag(SearchableTag)} operation from a
98 * {@link BasicSearchable}, in order to gain (more?) subtag children.
99 * <p>
100 * This method does not make sense for leaf tags.
101 *
102 * @return TRUE if it is complete
103 */
104 public boolean isComplete() {
105 return complete;
106 }
107
108 /**
109 * Non-complete, non-leaf tags can still be completed via a
110 * {@link BasicSearchable#fillTag(SearchableTag)} operation from a
111 * {@link BasicSearchable}, in order to gain (more?) subtag children.
112 * <p>
113 * This method does not make sense for leaf tags.
114 *
115 * @param complete
116 * TRUE if it is complete
117 */
118 public void setComplete(boolean complete) {
119 this.complete = complete;
120 }
121
122 /**
123 * The number of items that can be found with this tag if it is searched.
124 * <p>
125 * Will report the number of subtags by default.
126 *
127 * @return the number of items
128 */
129 public long getCount() {
130 long count = this.count;
131 if (count <= 0) {
132 count = children.size();
133 }
134
135 return count;
136 }
137
138 /**
139 * The number of items that can be found with this tag if it is searched,
140 * displayable format.
141 * <p>
142 * Will report the number of subtags by default.
143 *
144 * @return the number of items
145 */
146 public String getCountDisplay() {
147 long count = this.count;
148 if (count <= 0) {
149 count = children.size();
150 }
151
152 if (count > 999999) {
153 return count / 1000000 + "M";
154 }
155
156 if (count > 2000) {
157 return count / 1000 + "k";
158 }
159
160 return Long.toString(count);
161 }
162
163 /**
164 * The number of items that can be found with this tag if it is searched.
165 *
166 * @param count
167 * the new count
168 */
169 public void setCount(long count) {
170 this.count = count;
171 }
172
173 /**
174 * The number of stories result pages this tag contains, only make sense if
175 * {@link SearchableTag#isLeaf()} returns TRUE.
176 * <p>
177 * Will return -1 if the number is not yet known.
178 *
179 * @return the number of pages, or -1
180 */
181 public int getPages() {
182 return Math.max(-1, pages);
183 }
184
185 /**
186 * The number of stories result pages this tag contains, only make sense if
187 * {@link SearchableTag#isLeaf()} returns TRUE.
188 *
189 * @param pages
190 * the (positive or 0) number of pages
191 */
192 public void setPages(int pages) {
193 this.pages = Math.max(-1, pages);
194 }
195
196 /**
197 * This tag is a leaf tag, that is, it will not return other subtags with
198 * {@link BasicSearchable#fillTag(SearchableTag)} but will return stories
199 * with {@link BasicSearchable#search(SearchableTag)}.
200 *
201 * @return TRUE if it is
202 */
203 public boolean isLeaf() {
204 return pages > -2;
205 }
206
207 /**
208 * This tag is a leaf tag, that is, it will not return other subtags with
209 * {@link BasicSearchable#fillTag(SearchableTag)} but will return stories
210 * with {@link BasicSearchable#search(SearchableTag)}.
211 * <p>
212 * Will reset the number of pages to -1.
213 *
214 * @param leaf
215 * TRUE if it is
216 */
217 public void setLeaf(boolean leaf) {
218 pages = leaf ? -1 : -2;
219 }
220
221 /**
222 * The subtag children of this {@link SearchableTag}.
223 * <p>
224 * Never NULL.
225 * <p>
226 * Note that if {@link SearchableTag#isComplete()} returns false, you can
227 * still fill (more?) subtag children with a {@link BasicSearchable}.
228 *
229 * @return the subtag children, never NULL
230 */
231 public List<SearchableTag> getChildren() {
232 return children;
233 }
234
235 /**
236 * Add the given {@link SearchableTag} as a subtag child.
237 *
238 * @param tag
239 * the tag to add
240 */
241 public void add(SearchableTag tag) {
242 children.add(tag);
243 tag.parent = this;
244 }
245
246 /**
247 * This {@link SearchableTag} parent tag, or NULL if none.
248 *
249 * @return the parent or NULL
250 */
251 public SearchableTag getParent() {
252 return parent;
253 }
254
255 /**
256 * Display a DEBUG {@link String} representation of this object.
257 */
258 @Override
259 public String toString() {
260 String rep = name + " [" + id + "]";
261 if (!complete) {
262 rep += "*";
263 }
264
265 if (getCount() > 0) {
266 rep += " (" + getCountDisplay() + ")";
267 }
268
269 if (!children.isEmpty()) {
270 String tags = "";
271 int i = 1;
272 for (SearchableTag tag : children) {
273 if (!tags.isEmpty()) {
274 tags += ", ";
275 }
276
277 if (i > 10) {
278 tags += "...";
279 break;
280 }
281
282 tags += tag;
283 i++;
284 }
285
286 rep += ": " + tags;
287 }
288
289 return rep;
290 }
291 }