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.commons.pkg;
21  
22  import java.io.BufferedReader;
23  import java.io.BufferedWriter;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileWriter;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.InputStreamReader;
30  import java.net.URL;
31  import java.util.Hashtable;
32  import java.util.Iterator;
33  import java.util.LinkedList;
34  import java.util.List;
35  import java.util.Properties;
36  import java.util.SortedSet;
37  import java.util.StringTokenizer;
38  import java.util.TreeSet;
39  import java.util.zip.GZIPInputStream;
40  
41  /***
42   * 
43   */
44  public class PackageManager
45  {
46  
47      //--- Constant(s) ---
48  
49      //--- Data field(s) ---
50  
51  	/***
52  	 * A sorted list of PackageInfo objects.
53  	 */
54      private TreeSet packages = new TreeSet();
55  	private Hashtable packageListByProvides = new Hashtable();
56  
57      //--- Constructor(s) ---
58  
59  	public PackageManager()
60      {
61      }
62  
63      //--- Method(s) ---
64  
65  	/***
66  	 * Adds a package. If info is not valid or a package with the same
67  	 * name and version already exists the package is not added.
68  	 */
69  	public void add(PackageInfo info)
70  	{
71  		if (info.isValid()) {
72  			if (packages.contains(info)) {
73  				SortedSet tailSet = packages.tailSet(info);
74  				PackageInfo old = (PackageInfo)tailSet.first();
75  				if (old.isInstalled()) {
76  					old.setAvailable(true);
77  					// do not update already installed package info
78  					return;
79  				}
80  				else {
81  					remove(old);
82  					info.setNew(old.isNew());
83  				}
84  			}
85  			packages.add(info);
86  			info.setAvailable(true);
87  
88  			if (info.getProvides() != null) {
89  				StringTokenizer t
90  					= new StringTokenizer(info.getProvides(), ",");
91  				while (t.hasMoreTokens()) {
92  					String name = t.nextToken().trim();
93  					LinkedList list
94  						= (LinkedList)packageListByProvides.get(name);
95  					if (list == null) {
96  						list = new LinkedList();
97  						packageListByProvides.put(name, list);
98  					}
99  					list.add(info);
100 				}
101 			}
102 		}
103 	}
104 
105 	public PackageInfo[] getConflicts(PackageInfo info)
106 		throws ParseException
107 	{
108 		LinkedList packageTokens = new LinkedList();
109 		DefaultDependencyParser parser = new DefaultDependencyParser();
110 		AbstractToken token = parser.parseConflicts(info);
111 		if (token instanceof CommaToken) {
112 			AbstractToken[] depends = ((CommaToken)token).depends;
113 			for (int i = 0; i < depends.length; i++) {
114 				if (depends[i] instanceof PackageToken) {
115 					packageTokens.add(depends[i]);
116 				}
117 			}
118 		}
119 		else if (token instanceof PackageToken) {
120 			packageTokens.add(token);
121 		}
122 
123 		LinkedList infos = new LinkedList();
124 		for (Iterator it = packageTokens.iterator(); it.hasNext();) {
125 			addConflicts(infos, (PackageToken)it.next());
126 		}
127 		return (PackageInfo[])infos.toArray(new PackageInfo[0]);
128 	}
129 
130 	private void addConflicts(List list, PackageToken token) 
131 	{
132 		// packages
133 		for (Iterator it = tailSet(token.name).iterator(); it.hasNext();) {
134 			PackageInfo info = (PackageInfo)it.next();
135 			if (info.getPackage().equals(token.name)) {
136 				if (token.compareMode == null
137 					|| token.equalsVersion(info.compareToVersion
138 										   (token.version))) {
139 					// get package node
140 					list.add(info);
141 				}
142 			}
143 			else {
144 				break;
145 			}
146 		}
147 	}
148 
149 	/***
150 	 * Returns a list of urls.
151 	 *
152 	 * @return the empty string
153 	 */
154 	public String getDefaultSources()
155 	{
156 		return "";
157 	}
158 
159 	/***
160 	 *
161 	 * @exception ParseException thrown, if package file is invalid
162 	 * @exception UnsatisfiedDependenciesException thrown, if
163 	 * dependencies are not satisfied
164 	 */
165 	public PackageInfo[] getDependencies(PackageInfo info)
166 		throws ParseException, UnsatisfiedDependenciesException
167 	{
168 		DependencyGraph graph = new DependencyGraph(this);
169 		graph.add(info);
170 		graph.buildDependencies(new DefaultDependencyParser());
171 
172 		DefaultResolver r = new DefaultResolver(graph, true);
173 		r.resolve();
174 		return r.getRequired();
175 	}
176 
177 	/***
178 	 * Marks all packages that are not installed as unavailable. Marks
179 	 * all packages as not new.
180 	 */
181 	public void markAllUnavailable()
182 	{
183 		for (Iterator i = packages(); i.hasNext();) {
184 			PackageInfo info = (PackageInfo)i.next();
185 			info.setAvailable(info.isInstalled());
186 			info.setNew(false);
187 		}
188 	}
189 
190 	/***
191 	 * Reads package information from a control file.
192 	 *
193 	 * @param file the control file
194 	 */
195 	public void read(File file, Properties table) throws IOException
196 	{
197 		FileInputStream in = new FileInputStream(file);
198 		try {
199 			read(in, table);
200 		}
201 		finally {
202 			in.close();
203 		}
204 	}
205 
206 	public void read(File file) throws IOException
207 	{
208 		read(file, null);
209 	}	
210 
211 	/***
212 	 * Reads package information from a url.
213 	 *
214 	 * @param location the package file url
215 	 * @param downloadUrl the base location of the package files
216 	 */
217 	public void read(String location, Properties table) throws IOException
218 	{
219 		URL url = new URL(location);
220 		InputStream in = url.openStream();
221 		try {
222 			if (location.endsWith(".gz")) {
223 				in = new GZIPInputStream(url.openStream());
224 			}
225 			read(in, table);
226 		}
227 		finally {
228 			in.close();
229 		}
230 	}
231 
232 	/***
233 	 * Reads package information from a stream.
234 	 *
235 	 * @param inStream the stream
236 	 * @param flags additional flags that are added to the {@link
237 	 * PackageInfo} record; if null, flags are ignored
238 	 */
239 	public void read(InputStream inStream, Properties flags)
240 		throws IOException
241 	{
242 		BufferedReader in 
243 			= new BufferedReader(new InputStreamReader(inStream));
244 
245 		Properties p;
246 		while ((p = PackageInfoReader.readNext(in)) != null) {
247 			PackageInfo info = new PackageInfo(p);
248 			if (flags != null) {
249 				info.putAll(flags);
250 			}
251 			add(info);
252 		}
253 	}
254 
255 	public void remove(PackageInfo info)
256 	{
257 		packages.remove(info);
258 		if (info.getProvides() != null) {
259 			StringTokenizer t
260 				= new StringTokenizer(info.getProvides(), ",");
261 			while (t.hasMoreTokens()) {
262 				String name = t.nextToken().trim();
263 				LinkedList list
264 					= (LinkedList)packageListByProvides.get(name);
265 				if (list != null) {
266 					list.remove(info);
267 				}
268 			}
269 		}
270 	}
271 
272 	/***
273 	 * Removes all packages that are marked as unavailable.
274 	 */
275 	public void removeUnavailable()
276 	{
277 		LinkedList toRemove = new LinkedList();
278 		for (Iterator i = packages(); i.hasNext();) {
279 			PackageInfo info = (PackageInfo)i.next();
280 			if (!(info.isInstalled() || info.isAvailable())) {
281 				toRemove.add(info);
282 			}
283 		}
284 
285 		for (Iterator i = toRemove.iterator(); i.hasNext();) {
286 			remove((PackageInfo)i.next());
287 		}
288 	}
289 
290 	/***
291 	 * Returns an iterator over all {@link PackageInfo} objects.
292 	 */
293 	public Iterator packages()
294 	{
295 		return packages.iterator();
296 	}
297 
298 	/***
299 	 * Returns the number of packages.
300 	 */
301 	public int getPackageCount()
302 	{
303 		return packages.size();
304 	}
305 
306 	/***
307 	 * Returns a package by name.
308 	 */
309 	public PackageInfo getPackage(String packageName)
310 	{
311 			SortedSet set = tailSet(packageName);
312 			if (!set.isEmpty()) {
313 					PackageInfo info = (PackageInfo)set.first();
314 					if (info.getPackage().equals(packageName)) {
315 							return info;
316 					}
317 			}
318 				return null;
319 	}
320 
321 	public SortedSet tailSet(String packageName)
322 	{
323 		return packages.tailSet(new PackageInfo(packageName));
324 	}
325 
326 	/***
327 	 * Returns an array of packages that provide packageName.
328 	 */
329 	public PackageInfo[] getProviders(String packageName)
330 	{
331 		LinkedList list = (LinkedList)packageListByProvides.get(packageName);
332 		return (list != null) 
333 			? (PackageInfo[])list.toArray(new PackageInfo[0]) 
334 			: null;
335 	}
336 
337 	public void write(File file, Properties flags) throws IOException
338 	{
339 		BufferedWriter out = new BufferedWriter(new FileWriter(file));
340 		try {
341 			for (Iterator i = packages.iterator(); i.hasNext();) {
342 				PackageInfo info = (PackageInfo)i.next();
343 				if (flags == null || info.containsProperties(flags)) {
344 					PackageInfoWriter.write(out, info.getProperties());
345 				}
346 			}
347 		}
348 		finally {
349 			try {
350 				out.close();
351 			}
352 			catch (IOException e) {
353 			}
354 		}		
355 	}
356 
357 }
358