View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.gui.table;
21  
22  import java.util.Iterator;
23  import java.util.LinkedList;
24  import java.util.ListIterator;
25  
26  import org.xnap.XNap;
27  import org.xnap.event.ListEvent;
28  import org.xnap.gui.event.SwingListListener;
29  import org.xnap.gui.util.SwingSynchronizedCache;
30  import org.xnap.peer.Peer;
31  import org.xnap.plugin.Plugin;
32  import org.xnap.search.SearchFilter;
33  import org.xnap.search.SearchResult;
34  import org.xnap.search.SearchResultContainer;
35  import org.xnap.util.FileHelper;
36  import org.xnap.util.StringHelper;
37  import org.xnap.util.MultiHashtable;
38  
39  /***
40   * The model for the search table.
41   *
42   * @see xnap.gui.SearchResultPanel
43   */
44  public class SearchTableModel extends DefaultColumnTreeTableModel
45      implements SwingListListener {
46  
47      //--- Constant(s) ---
48  
49      //--- Data field(s) ---
50  
51      private SwingSynchronizedCache cache = new SwingSynchronizedCache(this);
52      private FilterHandler filter = new FilterHandler();
53  
54  	private MultiHashtable nodeByHash = new MultiHashtable();
55  
56      //--- Constructor(s) ---
57  
58      public SearchTableModel() 
59      {
60  		addColumns(createDefaultColumns());
61      }
62  
63      //--- Method(s) ---
64  
65      public static Column[] createDefaultColumns()
66      {
67  		return new Column[] {
68  			new Column("icon", XNap.tr("Filename"), TreeTableModel.class),
69  			new Column("filename", XNap.tr("Filename"),
70  					   SearchResultWrapper.class),
71  			new Column("path", XNap.tr("Path"), String.class, 
72  					   new StringCellRenderer()),
73  			new Column("filesize", XNap.tr("Filesize"), Long.class, 
74  					   new FilesizeCellRenderer()),
75  			new Column("peer", XNap.tr("Peer"), String.class, 
76  					   new StringCellRenderer()),
77  			new Column(XNap.tr("Link Speed"), Integer.class, 
78  					   new LinkSpeedCellRenderer()),
79  			new Column("plugin", XNap.tr("Plugin"), String.class),
80  			new Column("availability", XNap.tr("Availability"), Integer.class, 
81  					   new AvailabilityCellRenderer()),
82  			new Column("type", XNap.tr("Type"), String.class, 
83  					   new StringCellRenderer()),
84  			new Column("bitrate", XNap.tr("Bitrate"), Integer.class, 
85  					   new NumberCellRenderer()),
86  			new Column("length", XNap.tr("Length"), Integer.class,
87  					   new TimeCellRenderer()),
88  		};
89      }
90  
91      public SwingSynchronizedCache getListListener()
92      {
93  		return cache;
94      }
95  
96      /***
97       * Invoked when search results have been received.
98       */
99      public void itemsAdded(Object[] items)
100     {
101 		for (int i = 0; i < items.length; i++) {
102 			if (items[i] instanceof SearchResult) {
103 				SearchResult result = (SearchResult)items[i];
104 				group(result);
105 			}
106 			else if (items[i] instanceof TreeTableNode) {
107 				if (filter.matches((TreeTableNode)items[i])) {
108 					add((TreeTableNode)items[i]);
109 				}
110 			}
111 		}
112     }
113 
114     /***
115      * Groups results.
116      * 
117      * @return true, if result was added to the tree
118      */
119     public void group(SearchResult result)
120     {
121 		Object key = result.getHash();
122 		if (key != null) {
123 			for (ListIterator i = nodeByHash.getList(key).listIterator(); 
124 				 i.hasNext();) {
125 				LazyTreeTableNode node = (LazyTreeTableNode)i.next();
126 				SearchResult sr = (SearchResult)node.getData();
127 				if (sr.canGroup(result)) {
128 					if (!(sr instanceof SearchResultContainer)) {
129 						// create a new container
130 						SearchResultContainer c = sr.createContainer();
131 
132 						// add itself to container
133 						c.add(sr);
134 						
135 						// only set container in case second result
136 						// was accepted by the container, otherwise
137 						// the container is discarded
138 						if (c.add(result)) {
139 							// set container
140 							node.setData(c);
141 							
142 							// add original result to tree
143 							TreeTableNode leaf = new LeafTreeTableNode(sr);
144 							node.add(leaf);
145 							
146 							// add new result to tree
147 							leaf = new LeafTreeTableNode(result);
148 							node.add(leaf);
149 						}
150 					}
151 					else if (((SearchResultContainer)node.getData()).
152 							 add(result)) {
153 						TreeTableNode leaf = new LeafTreeTableNode(result);
154 						node.add(leaf);
155 					}
156 					return;
157 				}
158 			}
159 		}
160 
161 		LazyTreeTableNode node = new LazyTreeTableNode(this, result);
162 		if (key != null) {
163 			nodeByHash.put(key, node);
164 		}
165 		filter.add(node);
166 		if (filter.matches(node)) {
167 			add(node);
168 		}
169     }
170 
171     /***
172      * Never invoked.
173      */
174     public void itemsRemoved(Object[] items)
175     {
176     }
177 
178     public Object getValueAt(Object node, int column)
179     {
180 		if (node instanceof TreeTableNode) {
181 			SearchResult sr = (SearchResult)((TreeTableNode)node).getData();
182 			Peer p = sr.getPeer();
183 
184 			switch (column) {
185 			case 0:
186 				return (node instanceof LeafTreeTableNode) ? null 
187 					: sr.getIcon();
188 			case 1:
189 				// return sr.getShortFilename();
190 				return new SearchResultWrapper(sr);
191 			case 2:
192 				String[] path = sr.getPath();
193 				return (path != null) ? StringHelper.toString(path, "/") 
194   					: null;
195 			case 3:
196 				return new Long(sr.getFilesize());
197 			case 4:
198 				return (p != null) ? p.getName() : null;
199 			case 5:
200 				return new Integer((p != null) ? p.getLinkSpeed()  : -1);
201 			case 6:
202 				Plugin plugin = sr.getPlugin();
203 				return (plugin != null) ? plugin.getInfo().getName() : null;
204 			case 7:
205 				return new AvailabilityCellRenderer.Availability
206 					(sr.getAvailability(),
207 					 sr.getSourcesCount());
208 			case 8:
209 				return FileHelper.extension(sr.getShortFilename());
210 			default:
211 				return sr.get(getColumnAt(column).getName());
212 			}
213 		}
214 	
215 		return null;
216     }
217 
218     public void setFilter(SearchFilter f)
219     {
220 		filter.setFilter(f);
221     }
222 
223 	/***
224 	 * Invoked by {@link xnap.gui.SearchResultPanel} to set the tab title.
225 	 */
226 	public int size()
227 	{
228 		return getChildCount(getRoot());
229 	}
230 
231     //--- Inner Class(es) ---
232 
233     private class FilterHandler
234     {
235 
236 		private LinkedList data = new LinkedList();
237 		private SearchFilter filter;
238 
239 		public void add(TreeTableNode node)
240 		{
241 			data.add(node);
242 		}
243 	
244 		public Iterator iterator()
245 		{
246 			return data.iterator();
247 		}
248 
249 		public boolean matches(TreeTableNode node)
250 		{
251 			return (filter == null 
252 					|| filter.matches((SearchResult)
253 									  ((TreeTableNode)node).getData()));
254 		}
255 
256 		public void setFilter(SearchFilter filter)
257 		{
258 			this.filter = filter;
259 			clear();
260 			//  	    Runnable r = new Runnable 
261 			//  		{
262 			//  		    public void run()
263 			//  		    {
264 			//  		    }
265 			//  		};
266 			//  	    Thread t = new Thread("Inserter", 
267 			for (Iterator i = iterator(); i.hasNext();) {
268 				ListEvent event 
269 					= new ListEvent(this, ListEvent.ITEM_ADDED, i.next());
270 				cache.itemAdded(event);
271 			}
272 		}
273 
274     }
275 
276 }