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