f73dd15d7ce627fc3d3f7a72eb16959db52c96a6
[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, int)}
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, int)}
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 * The fully qualified tag name, which can be displayed to the user.
97 * <p>
98 * It will display all the tags that lead to this one as well as this one.
99 *
100 * @return the fully qualified name
101 */
102 public String getFqName() {
103 if (parent != null) {
104 return parent.getFqName() + " / " + name;
105 }
106
107 return name;
108 }
109
110 /**
111 * Non-complete, non-leaf tags can still be completed via a
112 * {@link BasicSearchable#fillTag(SearchableTag)} operation from a
113 * {@link BasicSearchable}, in order to gain (more?) subtag children.
114 * <p>
115 * This method does not make sense for leaf tags.
116 *
117 * @return TRUE if it is complete
118 */
119 public boolean isComplete() {
120 return complete;
121 }
122
123 /**
124 * Non-complete, non-leaf tags can still be completed via a
125 * {@link BasicSearchable#fillTag(SearchableTag)} operation from a
126 * {@link BasicSearchable}, in order to gain (more?) subtag children.
127 * <p>
128 * This method does not make sense for leaf tags.
129 *
130 * @param complete
131 * TRUE if it is complete
132 */
133 public void setComplete(boolean complete) {
134 this.complete = complete;
135 }
136
137 /**
138 * The number of items that can be found with this tag if it is searched.
139 * <p>
140 * Will report the number of subtags by default.
141 *
142 * @return the number of items
143 */
144 public long getCount() {
145 long count = this.count;
146 if (count <= 0) {
147 count = children.size();
148 }
149
150 return count;
151 }
152
153 /**
154 * The number of items that can be found with this tag if it is searched.
155 *
156 * @param count
157 * the new count
158 */
159 public void setCount(long count) {
160 this.count = count;
161 }
162
163 /**
164 * The number of stories result pages this tag contains, only make sense if
165 * {@link SearchableTag#isLeaf()} returns TRUE.
166 * <p>
167 * Will return -1 if the number is not yet known.
168 *
169 * @return the number of pages, or -1
170 */
171 public int getPages() {
172 return Math.max(-1, pages);
173 }
174
175 /**
176 * The number of stories result pages this tag contains, only make sense if
177 * {@link SearchableTag#isLeaf()} returns TRUE.
178 *
179 * @param pages
180 * the (positive or 0) number of pages
181 */
182 public void setPages(int pages) {
183 this.pages = Math.max(-1, pages);
184 }
185
186 /**
187 * This tag is a leaf tag, that is, it will not return other subtags with
188 * {@link BasicSearchable#fillTag(SearchableTag)} but will return stories
189 * with {@link BasicSearchable#search(SearchableTag, int)}.
190 *
191 * @return TRUE if it is
192 */
193 public boolean isLeaf() {
194 return pages > -2;
195 }
196
197 /**
198 * This tag is a leaf tag, that is, it will not return other subtags with
199 * {@link BasicSearchable#fillTag(SearchableTag)} but will return stories
200 * with {@link BasicSearchable#search(SearchableTag, int)}.
201 * <p>
202 * Will reset the number of pages to -1.
203 *
204 * @param leaf
205 * TRUE if it is
206 */
207 public void setLeaf(boolean leaf) {
208 pages = leaf ? -1 : -2;
209 }
210
211 /**
212 * The subtag children of this {@link SearchableTag}.
213 * <p>
214 * Never NULL.
215 * <p>
216 * Note that if {@link SearchableTag#isComplete()} returns false, you can
217 * still fill (more?) subtag children with a {@link BasicSearchable}.
218 *
219 * @return the subtag children, never NULL
220 */
221 public List<SearchableTag> getChildren() {
222 return children;
223 }
224
225 /**
226 * Add the given {@link SearchableTag} as a subtag child.
227 *
228 * @param tag
229 * the tag to add
230 */
231 public void add(SearchableTag tag) {
232 children.add(tag);
233 tag.parent = this;
234 }
235
236 /**
237 * This {@link SearchableTag} parent tag, or NULL if none.
238 *
239 * @return the parent or NULL
240 */
241 public SearchableTag getParent() {
242 return parent;
243 }
244
245 /**
246 * Display a DEBUG {@link String} representation of this object.
247 */
248 @Override
249 public String toString() {
250 String rep = name + " [" + id + "]";
251 if (!complete) {
252 rep += "*";
253 }
254
255 if (getCount() > 0) {
256 rep += " (" + getCount() + ")";
257 }
258
259 if (!children.isEmpty()) {
260 String tags = "";
261 int i = 1;
262 for (SearchableTag tag : children) {
263 if (!tags.isEmpty()) {
264 tags += ", ";
265 }
266
267 if (i > 10) {
268 tags += "...";
269 break;
270 }
271
272 tags += tag;
273 i++;
274 }
275
276 rep += ": " + tags;
277 }
278
279 return rep;
280 }
281 }