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.ArrayList;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  /**
30   * A Key that knows where the data is in the real file.
31   * 
32   * @see gnu.lgpl.License The GNU Lesser General Public License for details.
33   * @author DM Smith
34   */
35  public class TreeKey extends AbstractKeyList {
36      /**
37       * Setup with the key name and positions of data in the file
38       *
39       * @param name the key for this TreeKey 
40       * @param parent the parent node for this TreeKey
41       */
42      public TreeKey(String name, Key parent) {
43          super(name);
44          this.parent = parent;
45          this.children = new ArrayList<Key>();
46      }
47  
48      /**
49       * Setup with the key name. Use solely for searching.
50       *
51       * @param text the key for this TreeKey 
52        */
53      public TreeKey(String text) {
54          this(text, null);
55      }
56  
57      /* (non-Javadoc)
58       * @see org.crosswire.jsword.passage.Key#canHaveChildren()
59       */
60      public boolean canHaveChildren() {
61          return true;
62      }
63  
64      /* (non-Javadoc)
65       * @see org.crosswire.jsword.passage.Key#getChildCount()
66       */
67      public int getChildCount() {
68          return children.size();
69      }
70  
71      /* (non-Javadoc)
72       * @see org.crosswire.jsword.passage.Key#getCardinality()
73       */
74      public int getCardinality() {
75          int cardinality = 1; // count this node
76          for (Key child : children) {
77              cardinality += child.getCardinality();
78          }
79  
80          return cardinality;
81      }
82  
83      @Override
84      public boolean isEmpty() {
85          return children.isEmpty();
86      }
87  
88      @Override
89      public boolean contains(Key key) {
90          if (children.contains(key)) {
91              return true;
92          }
93  
94          for (Key child : children) {
95              if (child.contains(key)) {
96                  return true;
97              }
98          }
99  
100         return false;
101     }
102 
103     /* (non-Javadoc)
104      * @see java.lang.Iterable#iterator()
105      */
106     public Iterator<Key> iterator() {
107         return new KeyIterator(this);
108     }
109 
110     /* (non-Javadoc)
111      * @see org.crosswire.jsword.passage.Key#addAll(org.crosswire.jsword.passage.Key)
112      */
113     public void addAll(Key key) {
114         children.add(key);
115     }
116 
117     /* (non-Javadoc)
118      * @see org.crosswire.jsword.passage.Key#removeAll(org.crosswire.jsword.passage.Key)
119      */
120     public void removeAll(Key key) {
121         children.remove(key);
122     }
123 
124     /* (non-Javadoc)
125      * @see org.crosswire.jsword.passage.Key#clear()
126      */
127     public void clear() {
128         children.clear();
129     }
130 
131     /* (non-Javadoc)
132      * @see org.crosswire.jsword.passage.Key#get(int)
133      */
134     public Key get(int index) {
135         return children.get(index);
136     }
137 
138     /* (non-Javadoc)
139      * @see org.crosswire.jsword.passage.Key#indexOf(org.crosswire.jsword.passage.Key)
140      */
141     public int indexOf(Key that) {
142         return children.indexOf(that);
143     }
144 
145     /* (non-Javadoc)
146      * @see org.crosswire.jsword.passage.Key#getParent()
147      */
148     public Key getParent() {
149         return parent;
150     }
151 
152     /** equality is tricky if comparing TreeKeys (as used by GenBooks) because some child keys can have the same name but different parents
153      */
154     @Override
155     public boolean equals(Object obj) {
156         // Since this can not be null
157         if (obj == null) {
158             return false;
159         }
160 
161         // Check that that is the same as this
162         // Don't use instanceOf since that breaks inheritance
163         if (!obj.getClass().equals(this.getClass())) {
164             return false;
165         }
166 
167         TreeKey otherTreeKey = (TreeKey) obj;
168         if (!getName().equals(otherTreeKey.getName())) {
169             return false;
170         }
171 
172         // names match so now work up the tree comparing parents
173         if (getParent() == null) {
174             return otherTreeKey.getParent() == null;
175         }
176 
177         // KeyTrees nodes can have the same name but different parents
178         return getParent().equals(otherTreeKey.getParent());
179     }
180 
181     /* (non-Javadoc)
182      * @see org.crosswire.jsword.passage.Key#blur(int, org.crosswire.jsword.passage.RestrictionType)
183      */
184     public void blur(int by, RestrictionType restrict) {
185         log.warn("attempt to blur a non-blur-able list");
186     }
187 
188     @Override
189     public TreeKey clone() {
190         return (TreeKey) super.clone();
191     }
192 
193     @Override
194     public String getRootName() {
195         String rootName = getName();
196         for (Key parentKey = this; parentKey != null && parentKey.getName().length() > 0; parentKey = parentKey.getParent()) {
197             rootName = parentKey.getName();
198         }
199         return rootName;
200     }
201 
202     @Override
203     public String getOsisRef() {
204         return getOsisID();
205     }
206 
207     @Override
208     public String getOsisID() {
209         StringBuilder b = new StringBuilder(100);
210         b.append(getName());
211         for (Key parentKey = this.getParent(); parentKey != null && parentKey.getName().length() > 0; parentKey = parentKey.getParent()) {
212             b.insert(0, "/");
213             b.insert(0, parentKey.getName());
214         }
215         // Remove the leading .
216         return b.toString();
217     }
218 
219     /**
220      * The parent of this key.
221      */
222     private Key parent;
223 
224     /**
225      * The immediate children of this tree node.
226      */
227     private List<Key> children;
228 
229     /**
230      * The log stream
231      */
232     private static final Logger log = LoggerFactory.getLogger(TreeKey.class);
233 
234     /**
235      * Serialization ID
236      */
237     private static final long serialVersionUID = -6560408145705717977L;
238 }
239