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 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * 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   * ID: $Id: BookSet.java 2054 2010-12-10 22:12:09Z dmsmith $
21   */
22  package org.crosswire.jsword.book;
23  
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.Iterator;
28  import java.util.Set;
29  import java.util.TreeSet;
30  
31  import org.crosswire.common.util.Filter;
32  
33  /**
34   * BookSet represents a collection of descriptions about Books which may be
35   * subsetted into other BookMetaDataSets. Each set is naturally ordered.
36   * 
37   * @see gnu.lgpl.License for license details.<br>
38   *      The copyright to this program is held by it's authors.
39   * @author DM Smith [dmsmith555 at yahoo dot com]
40   */
41  public class BookSet extends ArrayList<Book> implements Set<Book> {
42      public BookSet() {
43      }
44  
45      public BookSet(Collection<Book> books) {
46          this();
47          addAll(books);
48      }
49  
50      /**
51       * Gets the sorted set of all keys which can be used for groupings. These
52       * are all the property keys across the BookMetaDatas in this list.
53       * 
54       * @return the set of all keys which can be used for grouping.
55       */
56      public Set<String> getGroups() {
57          Set<String> results = new TreeSet<String>();
58          for (Book book : this) {
59              results.addAll(book.getProperties().keySet());
60          }
61          return results;
62      }
63  
64      /**
65       * Get the sorted set of all values for a particular key. If there is a
66       * BookMetaData that does not have a value for that key, then null will be
67       * in the set. This can be use to categorize books that don't have that key.
68       * For example, "Language" will return all the languages for this
69       * BookMetaDataList and null for which the language is unknown.
70       * 
71       * @param key
72       * @return the values for a particular key.
73       */
74      public Set<Object> getGroup(String key) {
75          Set<Object> results = new TreeSet<Object>();
76          for (Book book : this) {
77              Object property = book.getProperty(key);
78              if (property != null) {
79                  results.add(property);
80              }
81          }
82          return results;
83      }
84  
85      public BookSet filter(String key, Object value) {
86          return filter(new GroupFilter(key, value));
87      }
88  
89      /*
90       * (non-Javadoc)
91       * 
92       * @see java.util.List#add(int, java.lang.Object)
93       */
94      @Override
95      public void add(int index, Book element) {
96          // ignore the requested index
97          add(element);
98      }
99  
100     /*
101      * (non-Javadoc)
102      * 
103      * @see java.util.Collection#add(java.lang.Object)
104      */
105     @Override
106     public final boolean add(Book book) {
107         // Add the item only if it is not in the list.
108         // Add it into the list so that it is in sorted order.
109         int pos = Collections.binarySearch(this, book);
110         if (pos < 0) {
111             super.add(-pos - 1, book);
112             return true;
113         }
114         return false;
115     }
116 
117     /*
118      * (non-Javadoc)
119      * 
120      * @see java.util.Collection#addAll(java.util.Collection)
121      */
122     @Override
123     public final boolean addAll(Collection<? extends Book> c) {
124         // Might be better to add the list to the end
125         // and then sort the list.
126         // This can be revisited if the list performs badly.
127         boolean added = false;
128         for (Book book : c) {
129             if (add(book)) {
130                 added = true;
131             }
132         }
133         return added;
134     }
135 
136     /*
137      * (non-Javadoc)
138      * 
139      * @see java.util.List#addAll(int, java.util.Collection)
140      */
141     @Override
142     public final boolean addAll(int index, Collection<? extends Book> c) {
143         // Ignore the index
144         return addAll(c);
145     }
146 
147     /*
148      * (non-Javadoc)
149      * 
150      * @see java.util.List#set(int, java.lang.Object)
151      */
152     @Override
153     public Book set(int index, Book element) {
154         // remove the item at the index (keep it to return it),
155         // then insert the item into the sorted list.
156         Book item = remove(index);
157         add(element);
158         return item;
159     }
160 
161     public BookSet filter(Filter filter) {
162         // create a copy of the list and
163         // remove everything that fails the test.
164         BookSet listSet = (BookSet) clone();
165         Iterator<Book> iter = listSet.iterator();
166         while (iter.hasNext()) {
167             Book obj = iter.next();
168             if (!filter.test(obj)) {
169                 iter.remove();
170             }
171         }
172         return listSet;
173     }
174 
175     /**
176      * GroupFilter does the SQL traditional group by.
177      */
178     private static final class GroupFilter implements Filter {
179         public GroupFilter(String aKey, Object aValue) {
180             key = aKey;
181             value = aValue;
182         }
183 
184         public boolean test(Object obj) {
185             Book book = (Book) obj;
186             Object property = book.getProperty(key);
187             return property != null && property.equals(value);
188         }
189 
190         private String key;
191         private Object value;
192     }
193 
194     /**
195      * Serialization ID
196      */
197     private static final long serialVersionUID = 3258688806185154867L;
198 
199 }
200