| AbstractKeyList.java |
1 /**
2 * Distribution License:
3 * JSword is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU Lesser General Public License, version 2.1 or later
5 * as published by the Free Software Foundation. This program is distributed
6 * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
7 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU Lesser General Public License for more details.
9 *
10 * The License is available on the internet at:
11 * http://www.gnu.org/copyleft/lgpl.html
12 * or by writing to:
13 * Free Software Foundation, Inc.
14 * 59 Temple Place - Suite 330
15 * Boston, MA 02111-1307, USA
16 *
17 * Copyright: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 */
21 package org.crosswire.jsword.passage;
22
23 import java.util.Iterator;
24
25 /**
26 * An implementation of some of the easier methods from Key.
27 *
28 * @see gnu.lgpl.License for license details.<br>
29 * The copyright to this program is held by it's authors.
30 * @author Joe Walker [joe at eireneh dot com]
31 */
32 public abstract class AbstractKeyList implements Key {
33 /**
34 * Build an AbstractKeyList with the given name.
35 *
36 * @param name
37 */
38 protected AbstractKeyList(String name) {
39 this.name = name;
40 }
41
42 /* (non-Javadoc)
43 * @see org.crosswire.jsword.passage.Key#isEmpty()
44 */
45 public boolean isEmpty() {
46 return getCardinality() == 0;
47 }
48
49 /* (non-Javadoc)
50 * @see org.crosswire.jsword.passage.Key#contains(org.crosswire.jsword.passage.Key)
51 */
52 public boolean contains(Key key) {
53 return indexOf(key) >= 0;
54 }
55
56 /* (non-Javadoc)
57 * @see org.crosswire.jsword.passage.Key#retainAll(org.crosswire.jsword.passage.Key)
58 */
59 public void retainAll(Key key) {
60 Key shared = new DefaultKeyList();
61 shared.addAll(key);
62 retain(this, shared);
63 }
64
65 /**
66 * Utility to remove all the keys from alter that are not in base
67 *
68 * @param alter
69 * The key to remove keys from
70 * @param base
71 * The check key
72 */
73 protected static void retain(Key alter, Key base) {
74 Iterator<Key> it = alter.iterator();
75 while (it.hasNext()) {
76 Key sublist = it.next();
77 if (sublist.canHaveChildren()) {
78 retain(sublist, base);
79 if (sublist.isEmpty()) {
80 it.remove();
81 }
82 } else {
83 if (!base.contains(sublist)) {
84 it.remove();
85 }
86 }
87 }
88 }
89
90 @Override
91 public String toString() {
92 return getName();
93 }
94
95 /**
96 * Override the default name with a custom name. If the name is null then a
97 * name will be generated by concatenating the names of all the elements of
98 * this node.
99 */
100 public void setName(String name) {
101 this.name = name;
102 }
103
104 /* (non-Javadoc)
105 * @see org.crosswire.jsword.passage.Key#getName()
106 */
107 public String getName() {
108 if (name != null) {
109 return name;
110 }
111
112 DefaultKeyVisitor visitor = new NameVisitor();
113 KeyUtil.visit(this, visitor);
114 return visitor.toString();
115 }
116
117 /* (non-Javadoc)
118 * @see org.crosswire.jsword.passage.Key#getName(org.crosswire.jsword.passage.Key)
119 */
120 public String getName(Key base) {
121 return getName();
122 }
123
124 /* (non-Javadoc)
125 * @see org.crosswire.jsword.passage.Key#getRootName()
126 */
127 public String getRootName() {
128 return getName();
129 }
130
131 /* (non-Javadoc)
132 * @see org.crosswire.jsword.passage.Key#getOsisRef()
133 */
134 public String getOsisRef() {
135 DefaultKeyVisitor visitor = new OsisRefVisitor();
136 KeyUtil.visit(this, visitor);
137 return visitor.toString();
138 }
139
140 /* (non-Javadoc)
141 * @see org.crosswire.jsword.passage.Key#getOsisID()
142 */
143 public String getOsisID() {
144 DefaultKeyVisitor visitor = new OsisIDVisitor();
145 KeyUtil.visit(this, visitor);
146 return visitor.toString();
147 }
148
149 @Override
150 public boolean equals(Object obj) {
151 // Since this can not be null
152 if (obj == null) {
153 return false;
154 }
155
156 // Check that that is the same as this
157 // Don't use instanceOf since that breaks inheritance
158 if (!obj.getClass().equals(this.getClass())) {
159 return false;
160 }
161
162 return compareTo((Key) obj) == 0;
163 }
164
165 @Override
166 public int hashCode() {
167 return getName().hashCode();
168 }
169
170 /* (non-Javadoc)
171 * @see java.lang.Comparable#compareTo(java.lang.Object)
172 */
173 public int compareTo(Key that) {
174
175 if (this == that) {
176 return 0;
177 }
178
179 if (that == null) {
180 // he is empty, we are not so he is greater
181 return -1;
182 }
183
184 int ret = this.getName().compareTo(that.getName());
185
186 if (ret != 0) {
187 return ret;
188 }
189
190 // Compare the contents.
191 Iterator<Key> thisIter = this.iterator();
192 Iterator<Key> thatIter = that.iterator();
193
194 Key thisfirst = null;
195 Key thatfirst = null;
196
197 if (thisIter.hasNext()) {
198 thisfirst = thisIter.next();
199 }
200
201 if (thatIter.hasNext()) {
202 thatfirst = thatIter.next();
203 }
204
205 if (thisfirst == null) {
206 if (thatfirst == null) {
207 // we are both empty, and rank the same
208 return 0;
209 }
210 // i am empty, he is not so we are greater
211 return 1;
212 }
213
214 if (thatfirst == null) {
215 // he is empty, we are not so he is greater
216 return -1;
217 }
218
219 return thisfirst.getName().compareTo(thatfirst.getName());
220 }
221
222 @Override
223 public AbstractKeyList clone() {
224 AbstractKeyList clone = null;
225 try {
226 clone = (AbstractKeyList) super.clone();
227 } catch (CloneNotSupportedException e) {
228 assert false : e;
229 }
230 return clone;
231 }
232
233 /**
234 * The <code>NameVisitor</code> constructs a readable representation of the
235 * Passage.
236 */
237 static class NameVisitor extends DefaultKeyVisitor {
238 /**
239 * Create a <code>NameVisitor</code>.
240 */
241 public NameVisitor() {
242 buffer = new StringBuilder();
243 }
244
245 @Override
246 public void visitLeaf(Key key) {
247 buffer.append(key.getName());
248 buffer.append(AbstractPassage.REF_PREF_DELIM);
249 }
250
251 @Override
252 public String toString() {
253 String reply = buffer.toString();
254 if (reply.length() > 0) {
255 // strip off the final ", "
256 reply = reply.substring(0, reply.length() - AbstractPassage.REF_PREF_DELIM.length());
257 }
258
259 return reply;
260 }
261
262 protected StringBuilder buffer;
263 }
264
265 /**
266 * The <code>OsisRefVisitor</code> constructs a readable representation of
267 * the Passage, using OSIS names.
268 */
269 static class OsisRefVisitor extends NameVisitor {
270 @Override
271 public void visitLeaf(Key key) {
272 buffer.append(key.getOsisRef());
273 buffer.append(AbstractPassage.REF_PREF_DELIM);
274 }
275 }
276
277 /**
278 * The <code>OsisRefVisitor</code> constructs a readable representation of
279 * the Passage, using OSIS names.
280 */
281 static class OsisIDVisitor extends NameVisitor {
282 @Override
283 public void visitLeaf(Key key) {
284 buffer.append(key.getOsisID());
285 buffer.append(AbstractPassage.REF_OSIS_DELIM);
286 }
287
288 @Override
289 public String toString() {
290 String reply = super.toString();
291 if (reply.length() > 0) {
292 // strip off the final " "
293 reply = reply.substring(0, reply.length() - AbstractPassage.REF_OSIS_DELIM.length());
294 }
295
296 return reply;
297 }
298 }
299
300 /**
301 * The common user visible name for this work
302 */
303 private String name;
304
305 /**
306 * Serialization ID
307 */
308 private static final long serialVersionUID = 3858640507828137034L;
309 }
310