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.pkg;
21  
22  import java.util.Arrays;
23  import java.util.HashSet;
24  import java.util.Hashtable;
25  import java.util.Iterator;
26  import java.util.LinkedList;
27  import java.util.TreeMap;
28  
29  /***
30   * 
31   */
32  public class DependencyGraph
33  {
34  
35      //--- Constant(s) ---
36  
37      //--- Data field(s) ---
38  	
39  	private PackageManager manager;
40  
41  	private AndDependencyNode root;
42  	private TreeMap nodeByPackage = new TreeMap();
43  	private Hashtable nodeByToken = new Hashtable();
44  	private LinkedList unparsedNodes = new LinkedList();
45  
46      //--- Constructor(s) ---
47  
48  	public DependencyGraph(PackageManager manager)
49      {
50  		this.manager = manager;
51  		root = new AndDependencyNode("Root");
52      }
53  
54      //--- Method(s) ---
55  
56  	public void add(PackageInfo info)
57  	{
58   		DependencyNode node	= addNode(info);
59  		root.add(node);
60  	}
61  
62  	public void buildDependencies(DependencyParser parser) throws ParseException
63  	{
64  		while (!unparsedNodes.isEmpty()) {
65  			PackageDependencyNode node
66  				= (PackageDependencyNode)unparsedNodes.removeFirst();
67  
68  			try {
69  				// add depends
70  				AbstractToken token = parser.parseDepends(node.getPackage());
71  				if (token != null) {
72  					node.setDepends(add(token));
73  				}
74  
75  				// add conflicts
76  				token = parser.parseConflicts(node.getPackage());
77  				if (token != null) {
78  					node.setConflicts(new ConflictsDependencyNode
79  						("conflicts for " + node.getID(),
80  						 add(token)));
81  				}
82  			}
83  			catch (ParseException e) {
84  				throw new ParseException("Invalid package description for " 
85  										 + node.toString()
86  										 + "(" + e.getMessage() + ")");
87  			}
88   		}
89  	}
90  
91  	public DependencyNode getRoot()
92  	{
93  		return root;
94  	}
95  
96  	public Iterator preorderIterator()
97  	{
98  		return new PreorderIterator(root);
99  	}
100 
101  	private DependencyNode add(AbstractToken token)
102  	{
103 		DependencyNode node = (DependencyNode)nodeByToken.get(token.toString());
104 		if (node != null) {
105 			return node;
106 		}
107 		else if (token instanceof CommaToken) {
108 			return add((CommaToken)token);
109 		}
110 		else if (token instanceof PipeToken) {
111 			return add((PipeToken)token);
112 		}
113 		else if (token instanceof PackageToken) {
114 			return add((PackageToken)token);
115 		}
116 
117  		throw new RuntimeException("Unknown token: " + token);
118  	}
119 
120 	private DependencyNode[] add(AbstractToken[] tokens)
121 	{
122 		DependencyNode[] nodes = new DependencyNode[tokens.length];
123 		for (int i = 0; i < tokens.length; i++) {
124 			DependencyNode node = (DependencyNode)nodeByToken.get(tokens[i]);
125 			nodes[i] = (node != null) ? node : add(tokens[i]);
126 		}
127 		return nodes;
128 	}
129 
130 	private DependencyNode add(CommaToken token)
131 	{
132 		LinkedList list = new LinkedList(Arrays.asList(add(token.depends)));
133 		AndDependencyNode node = new AndDependencyNode(token.toString(), list);
134 		return addNode(token.toString(), node);
135 	}
136 
137 	private DependencyNode add(PipeToken token)
138 	{
139 		LinkedList list = new LinkedList(Arrays.asList(add(token.depends)));
140 		OrDependencyNode node = new OrDependencyNode(token.toString(), list);
141 		return addNode(token.toString(), node);
142 	}
143 
144 	private DependencyNode add(PackageToken token)
145 	{
146 		LinkedList list = new LinkedList();
147 
148 		// packages
149 		for (Iterator i = manager.tailSet(token.name).iterator(); i.hasNext();) {
150 			PackageInfo info = (PackageInfo)i.next();
151 			if (info.getPackage().equals(token.name)) {
152 				if (token.compareMode == null
153 					|| token.equalsVersion(info.compareToVersion
154 										   (token.version))) {
155 					// get package node
156 					list.add(addNode(info));
157 				}
158 			}
159 			else {
160 				break;
161 			}
162 		}
163 	
164 		// provides
165 		if (token.compareMode == null) {
166 			PackageInfo[] providers = manager.getProviders(token.name);
167 			if (providers != null) {
168 				for (int i = 0; i < providers.length; i++) {
169 					list.add(addNode(providers[i]));
170 				}
171 			}
172 		}
173 
174 		if (list.size() == 0) {
175 			return addNode(token.toString(), 
176 						   new UnsatisfiedDependencyNode(token.toString()));
177 		}
178 		else if (list.size() == 1) {
179 			return addNode(token.toString(), (DependencyNode)list.get(0));
180 		}
181 		else {
182 			return addNode(token.toString(), 
183 						   new OrDependencyNode(token.toString(), list));
184 		}
185 	}
186 
187 	private DependencyNode addNode(String token, DependencyNode node)
188 	{
189 		nodeByToken.put(token, node);
190 		return node;
191 	}
192 
193 	private DependencyNode addNode(PackageInfo info)
194 	{
195 		DependencyNode node = (DependencyNode)nodeByPackage.get(info);
196 		if (node == null) {
197 			node = new PackageDependencyNode(info);
198 			nodeByPackage.put(info, node);
199 			unparsedNodes.add(node);
200 		}
201 		return node;
202 	}
203 
204 	//--- Inner Class(es) ---
205 
206 	public static class PreorderIterator implements Iterator 
207 	{
208 		private HashSet visited = new HashSet();
209 		private LinkedList stack = new LinkedList();
210 
211 		public PreorderIterator(DependencyNode rootNode) 
212 		{
213 			for (Iterator i = rootNode.children(); i.hasNext();) {
214 				DependencyNode node = (DependencyNode)i.next();
215 				visited.add(node);
216 				stack.addFirst(node);
217 			}
218 		}
219 
220 		public boolean hasNext() 
221 		{
222 			return !stack.isEmpty();
223 		}
224 
225 		public Object next() 
226 		{
227 			DependencyNode parent = (DependencyNode)stack.removeFirst();
228 
229 			for (Iterator i = parent.children(); i.hasNext();) {
230 				DependencyNode node = (DependencyNode)i.next();
231 				
232 				if (!visited.contains(node)) {
233 					visited.add(node);
234 					stack.addFirst(node);
235 				}
236 			}
237 
238 			return parent;
239 		}
240 
241 		public void remove()
242 		{
243 			throw new UnsupportedOperationException();
244 		}
245 
246     }
247 
248 }
249