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