View Javadoc

1   /*
2    * Copyright 2001,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.jjar;
18  
19  import java.util.Properties;
20  import java.util.StringTokenizer;
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Stack;
25  import java.util.Map;
26  import java.util.HashMap;
27  
28  import java.io.StringWriter;
29  import java.io.Writer;
30  import java.io.InputStream;
31  import java.io.InputStreamReader;
32  import java.io.BufferedInputStream;
33  import java.io.IOException;
34  
35  import java.net.URL;
36  import java.net.URLConnection;
37  
38  import org.xml.sax.*;
39  import uk.co.wilson.xml.MinML2;
40  
41  /**
42   *  Repository implementation that supports XML repository
43   *  definitions.  Uses a great small little lightweight SAX
44   *  parser - but it's not clear what it doesn't support, so
45   *  be careful
46   *
47   *  @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
48   *  @version $Id: RepositoryXML.java 155454 2005-02-26 13:23:34Z dirkv $
49   */
50  public class RepositoryXML extends MinML2 implements Repository
51  {
52  	/** list of packages */
53      private HashMap packageMap = new HashMap();
54  	int packageCount = 0;
55  
56  	/** handles package dependency generation */
57  	private static DependencyEngine de = new DependencyEngine();
58  
59  	/** root of our document tree */
60      private Node root = null;
61  
62  	/** stack used for SAX parsing */
63      private Stack  stack = new Stack();
64   
65  	/** user for SAX parsing to avoid character events */
66      private StringWriter saxWriter = null;
67  
68      /** string constants for use in data structure */
69  	private static String PACKAGENAME = "packagename";
70  	private static String DEFAULTVERSION = "defaultversion";
71  	private static String NODE = "node";
72  	private static String DESCRIPTION = "description";
73  	private static String HREF = "href";
74  	private static String VERSION = "version";  
75      private static String VERSION_INFO = "version_info";
76      private static String VERSION_JAR = "version_jar";
77      private static String DEP_PACKAGE = "dep_package";
78      private static String DEP_VERSION = "dep_version";
79      private static String VERSION_DEPS = "version_deps";
80      private static String VERSION_ARRAY = "version_array";
81   
82  	/**
83  	 *  loads the object with the XML data from the
84  	 *  passed in URL.  Invokes the SAX parser.
85  	 *
86  	 *  @param url location of the repository.xml
87  	 */
88      public void load( URL url )
89      {
90          load( url, packageMap );
91      }
92  
93      public void load( URL url, Map pkgMap )
94      {
95          try 
96          {
97              URLConnection conn = url.openConnection();
98              InputStream is = conn.getInputStream();
99  
100             parse(new InputStreamReader(new BufferedInputStream( is)));
101             process( pkgMap );
102         }
103         catch ( IOException e) 
104         {
105             System.out.println("IOException: " + e);
106             e.printStackTrace();
107         }
108         catch ( SAXException e) 
109         {
110             System.out.println("SAXException: " + e);
111             e.printStackTrace();
112         }
113         catch ( Throwable e) 
114         {
115             System.out.println("Other Exception: " + e);
116             e.printStackTrace();
117         }
118     }
119  
120 	/**
121 	 *  returns a list of dependencies for
122 	 *  a given package.  The list contains
123 	 *  Maps, with 'package' and 'version'
124 	 *  as the keys for the necessary info.
125 	 *
126 	 *  @param pkg package to get dep list for
127 	 *  @return List of Maps
128 	 */
129 	public List getDependencyList( String pkg, String version )
130     {
131         if( isPackage( pkg ))
132             return de.getDependencies( pkg + ":" + version );
133 
134         return new ArrayList();
135     }
136 
137     /**
138      *  returns an iterator over the
139      *   list of package names
140      */ 
141     public Iterator getPackageListIter()
142     {
143         return  packageMap.keySet().iterator();
144     }
145 
146     /**
147      *  returns the number of packages known to
148      *  the repository
149      */
150     public int getPackageCount()
151     {
152         return packageMap.size();
153     }
154 
155     /**
156      *  returns the default version of the package to the caller
157      */
158     public String getPackageDefaultVersion( String pkg )
159     {
160         if (isPackage( pkg ) )
161         {
162             HashMap hpack = (HashMap) packageMap.get( pkg );
163             return (String) hpack.get( DEFAULTVERSION);
164         }
165 
166         return null;
167     }
168 
169     /**
170      *  returns the info/desc string to the caller
171      */
172     public String getPackageDescription( String pkg )
173     {
174         if (isPackage( pkg ) )
175         {
176             HashMap hpack = (HashMap) packageMap.get( pkg );
177             return (String) hpack.get( DESCRIPTION);
178         }
179 
180         return "";
181     }
182 
183     /**
184      *  returns the 'fetch' target (jarname) for the 
185      *  given package : version
186      */
187     public String getFetchTarget( String pkg, String version )
188     {
189 
190         /*
191          *  first see if we know about this package.
192          */
193 
194         if (!isPackage( pkg ))
195             return null;
196 
197         /*
198          * next, get the package info and see if we have this version
199          */
200 
201         Map hp = (HashMap) packageMap.get( pkg );
202         Iterator i =((ArrayList) hp.get( VERSION_ARRAY )).iterator();        
203         
204         while( i.hasNext() )
205         {
206             HashMap vi = (HashMap) i.next();
207             String ver = (String) vi.get( VERSION );
208 
209             if (ver != null)
210             {
211                 if (version.equals( ver ) )
212                 {
213                     String out = (String) vi.get(VERSION_JAR);
214                     return out;
215                 }
216             }
217         }
218 
219         return null;
220     }
221 
222     /**
223      *  determines if something is a known package
224      */
225     public boolean isPackage( String pkg )
226     {
227 		Object o = packageMap.get( pkg );
228 
229 		if( o != null)
230 			return true;
231 
232 		return false;
233     }
234 
235     /**
236      *  returns a given property 
237      *  not implemented
238      */
239     public String getProp( String s )
240     {
241         return s;
242     }
243 
244     /**
245      *  returns a list of the know versions available
246      *  for this package
247      */
248     public List getPackageVersionList( String pkg )
249     {
250         if(!isPackage( pkg ))
251            return null;
252      
253         Map hp = (HashMap) packageMap.get( pkg );
254 
255         ArrayList verlist = new ArrayList();
256 
257         Iterator i =((ArrayList) hp.get( VERSION_ARRAY )).iterator();
258         
259         while( i.hasNext() )
260         {
261             verlist.add( ( (HashMap)i.next() ).get( VERSION ) );
262         }
263 
264         return verlist;
265     }
266 
267     private List makeList( String thing )
268     {
269         StringTokenizer st = new StringTokenizer( thing, ", ");
270 
271         List list = new ArrayList();
272 
273         while(st.hasMoreTokens() )
274         {
275             list.add( st.nextToken().trim() );
276         }
277 
278         return list;
279     }
280 
281 	/**
282 	 *  takes the intermediary form of input data, and loads the
283 	 *  internal data structures for use later.  This needs to be
284 	 *  called right after parsing, and before use.
285 	 */
286     private void process( Map pkgMap )
287     {
288         /*
289          *  need to build a package list.  Get the package groups.
290          */
291 
292 		List packagegroup = getDocNodeList( root, "packagegroup");
293         
294         if (packagegroup == null)
295             System.out.println("Packagegroup == null");
296 
297         //System.out.println("packagegroup has " + packagegroup.size() + "elements");
298         
299         Iterator i = packagegroup.iterator();
300 
301         while( i.hasNext() )
302         {
303             Node pkggrp = (Node) i.next();
304 
305             //System.out.println("Processing packagegroup : " + pkggrp.getAttribute("name") );
306 
307 			List packages = getDocNodeList( pkggrp, "package");
308 
309 			/*
310 			 * for each package...
311 			 */
312 
313 			Iterator ii = packages.iterator();
314 
315 			while( ii.hasNext() )
316 			{
317 				processPackageNode( (Node) ii.next(), pkgMap );
318 			}
319         }
320     }
321 	
322 	private void processPackageNode( Node pkg, Map pkgMap )
323 	{
324 		HashMap pkginfo = new HashMap();
325 
326 		/*
327 		 *  start with the basics
328 		 */
329 
330 		pkginfo.put( PACKAGENAME, pkg.getAttribute("name") );
331 		pkginfo.put( DEFAULTVERSION, pkg.getAttribute("default"));
332 		pkginfo.put( NODE, pkg );
333 
334         /*
335          *  does this have a definition here or remote?
336          */
337 
338         Node def = getDocNode( pkg, "definition");
339 
340         if (def != null)
341         {
342             /*
343              * now get the information node
344              */
345 
346             Node info = getDocNode( def, "info/desc");
347 
348             if( info != null)
349                 pkginfo.put( DESCRIPTION, info.getValue());
350 
351             info = getDocNode( def, "info/href");
352 
353             if( info != null)
354                 pkginfo.put( HREF, info.getValue());
355             
356             /*
357              *  process version info
358              */
359             
360             List  versionlist = getDocNodeList( def, "versionset/version");
361             ArrayList versionArray = new ArrayList();
362             
363             Iterator v = versionlist.iterator();
364             
365             while( v.hasNext() )
366             {
367                 Node n = (Node) v.next();
368                 
369                 HashMap vi = new HashMap();
370                 
371                 vi.put( VERSION, n.getAttribute("version") );
372                 
373                 Node nn = getDocNode( n, "note");
374                 vi.put( VERSION_INFO, nn.getValue() );
375                 
376                 nn = getDocNode( n, "jar");
377                 vi.put( VERSION_JAR, nn.getValue() );
378                 
379                 /*  the dependencies */
380                 
381                 ArrayList deplist = new ArrayList();
382                 
383                 List deps = getDocNodeList( n, "dependencies/dep");
384                 
385                 if (deps != null)
386                 {
387                     Iterator ii = deps.iterator();
388                     
389                     while( ii.hasNext() )
390                     {
391                         HashMap h = new HashMap();
392                         Node ndep = (Node) ii.next();
393                         
394                         h.put( DEP_PACKAGE, ndep.getAttribute("package") );
395                         h.put( DEP_VERSION, ndep.getAttribute("version") );
396                         
397                         deplist.add( h );
398                     }
399                     
400                     vi.put( VERSION_DEPS, deplist );
401                 }
402                 
403                 versionArray.add( vi );
404  
405                 /*
406                  *  add the package:version to the dependency engine
407                  */
408                 
409                 String token = pkginfo.get( PACKAGENAME ) + ":" +  (String) vi.get( VERSION );
410                 
411                 // System.out.println("Adding " + token + " to dependency engine.");
412                 
413                 ArrayList depar = new ArrayList();
414                 
415                 Iterator depiter = deplist.iterator();
416                 
417                 while( depiter.hasNext() )
418                 {
419                     HashMap h = (HashMap) depiter.next();
420                     
421                     String deptoken = (String) h.get(DEP_PACKAGE) + ":" +  (String) h.get(DEP_VERSION);
422                     depar.add( deptoken );
423                 }
424 
425                 try
426                 {
427                     de.addProject( token, depar, token );
428                 }
429                 catch( Exception e )
430                     {}
431 
432             }
433 
434             pkginfo.put( VERSION_ARRAY, versionArray );
435 
436             /*
437              *  add the info to the packageMap
438              */
439             
440             pkgMap.put( pkginfo.get( PACKAGENAME ), pkginfo );
441             
442             //		dumpPackageInfo( pkginfo );
443         }
444         else
445         {
446             /*
447              * is this a remote repository? 
448              */
449             
450             Node remote = getDocNode( pkg, "remotedefinition");
451 
452             if (remote != null)
453             {
454                 System.out.println( "Note package '" + ( (String) pkginfo.get( PACKAGENAME ) ) 
455                                     + "' defining repository is remote, coming from " + remote.getValue() );
456             
457                 /*
458                  *  now recursively get the info
459                  */
460 
461                 try
462                 {
463                     RepositoryXML rep = new RepositoryXML();
464                     rep.load( new URL( remote.getValue() ), pkgMap );
465                 }
466                 catch( Exception ee )
467                     {}
468             }
469             else
470             {
471                 System.out.println("Malformed repository : neither <definition> or <remotedefinition>");
472             }
473             
474                 
475         }
476 	}
477 
478     /**
479      *  simple dumper for debugging
480      */
481     private void dumpPackageInfo( Map h )
482 	{
483 		System.out.println("Package : " + h.get( PACKAGENAME ) );
484 		System.out.println("  default version : " + h.get(DEFAULTVERSION ));
485 		System.out.println("     description  : " + h.get(DESCRIPTION ));
486 		System.out.println("             href : " + h.get(HREF ));
487 
488         System.out.println("      version info -> " );
489 
490         ArrayList vl = (ArrayList) h.get( VERSION_ARRAY );
491         Iterator i = vl.iterator();
492         
493         while( i.hasNext() )
494         {
495             HashMap vi = (HashMap) i.next();
496 
497             System.out.println("      ver : " + vi.get( VERSION ) );
498             System.out.println("     info : " + vi.get( VERSION_INFO ) );
499             System.out.println("      jar : " + vi.get( VERSION_JAR ) );
500         }
501 	}
502 
503 
504     /**
505      *  Returns a given node from the document tree.
506      *  if a multivalued node, will return the first
507      *
508      *  @param root  node to start seaching on
509      *  @param path node path, a la "foo/bar/woogie"
510      *  @return node or null
511      */
512 	private Node getDocNode( Node root, String path )
513 	{
514 		List ar = getDocNodeList(root, path );
515 
516 		if (ar == null || ar.size() == 0)
517 			return null;
518 
519 		return (Node) ar.get(0);
520 	}
521 
522     /**
523      *  returns the node list for a given path relative to the
524      *  passed in node
525      *
526      *  @param root node to start from
527      *  @param path note path
528      *  @return List of nodes that match
529      */
530 	private List getDocNodeList( Node root, String path)
531 	{
532 		StringTokenizer st = new StringTokenizer( path, "/");
533 		
534 		Node n = root;
535 		ArrayList ar = null;
536 
537 		while( st.hasMoreTokens() )
538 		{
539 			String token = st.nextToken();
540 			
541 			ar = n.getChildren(token);
542 			
543 			if (ar == null || ar.size() == 0)
544 				return null;
545 
546 			n = (Node) ar.get(0);
547 		}
548 
549 		return ar;
550 	}
551 
552     /**
553      *  start element event handler for SAX.  
554      */
555     public Writer startElement( String namespaceURI, String localName, String qName, Attributes atts,  Writer writer)
556         throws SAXException
557     {
558         /*
559          *  get the top element on the list
560          */
561 		
562 		Node parent = null;
563 		Node element = new Node( qName );
564 		
565         if( root == null)
566 		{
567             root = element;
568         }
569         else
570         {
571             /*
572              * get parent node
573              */
574 
575             try
576             {
577                 parent = (Node) stack.peek();
578             }
579             catch(Exception e)
580                 {}
581         }
582 
583         /*
584          *  everything is a list
585          */
586 
587         if (parent != null)
588         {    
589 			parent.addChildNode( element);
590         }
591 
592         /*
593          *  now add our attributes
594          */
595 
596         for (int i = 0; i < atts.getLength(); i++)
597         {
598             element.setAttribute( atts.getQName(i), atts.getValue(i) );
599         }
600 
601         /*
602          *  now put this element on the stack for later
603          */
604 
605         stack.push(element);
606         
607         saxWriter = new StringWriter();
608         return saxWriter;
609     }
610    
611 
612     /**
613      *  end element event handler for SAX parsing
614      */
615     public void endElement( String namespaceURI, String localName, String qName)
616         throws SAXException
617     {
618         try
619         {
620             Node element = (Node) stack.pop();
621             
622             String s = saxWriter.toString();
623 
624             if ( s.length() > 0)
625                 element.setValue( s );
626         }
627         catch( Exception e )
628         {
629             System.out.println("endElement : " + e );
630         }
631     }
632     
633     public void fatalError (SAXParseException e) 
634       throws SAXException 
635     {
636         System.out.println("RepositoryXML.fatalError(): " + e);
637         throw e;
638     }
639     
640     /**
641      *  used for debugging
642      */
643     public static void main ( String[] args) 
644     {
645       RepositoryXML rep = new RepositoryXML();
646 
647       try
648       {
649           rep.load( new URL("file://" + args[0] ));
650       }
651       catch( Exception e )
652           { System.out.println(e );}
653 
654       
655       List al = rep.getDependencyList( "ant", "1.3");
656 
657       Iterator i = al.iterator();
658 
659       System.out.println("dep for ant 1.3 ");
660 
661       while( i.hasNext() )
662       {
663           System.out.println("  " + (String) i.next() );
664       }
665               
666   }
667     /**
668      *  meethod for debugging parsing
669      */
670     private static  void show( String indent,  Map map )
671     {
672         System.out.println("");
673 
674         Iterator i = map.keySet().iterator();
675 
676         while(i.hasNext() )
677         {
678             String s = (String) i.next();
679             Object o = map.get( s );
680             System.out.print( indent + "<" + s + ">");
681 
682             if ( o instanceof Map )
683             {
684                 show( indent + "  ", (Map) o);
685             }
686             else if ( o instanceof List )
687             {
688                 Iterator j = ((List) o).iterator();
689                 
690                 while( j.hasNext() )
691                 {
692                     Object oo = j.next();
693                     
694                     if (oo instanceof Map)
695                         show( indent + "  ", (Map) oo);
696                     else
697                         System.out.println("woo!" + oo.getClass());
698                 }
699             }
700             else
701             {
702                 System.out.println( map.get( s ));
703             }
704                     
705       }
706     }
707 
708 	/**
709 	 *  small class to hold our node information for
710 	 *  XML processing
711 	 */
712 	class Node 
713 	{
714 		String name = null;
715 		String value = "";
716 
717 		HashMap attributes = new HashMap();
718 		HashMap children = new HashMap();
719 
720 		Node( String name )
721 		{
722 			this.name = name;
723 		}
724 
725 		void setValue( String val )
726 		{
727 			value = val;
728 		}
729 		
730 		String getValue()
731 		{
732 			return value;
733 		}
734 
735 		void setAttribute( String key, String val )
736 		{
737 			attributes.put( key, val );
738 		}
739 		
740 		String getAttribute( String key )
741 		{
742 			return (String) attributes.get( key );
743 		}
744 
745 		void addChildNode( Node node )
746 		{
747 			ArrayList ar = (ArrayList) children.get( node.name );
748 
749 			if (ar == null)
750 				ar = new ArrayList();
751 
752 			ar.add( node );
753 
754 			children.put( node.name, ar );
755 		}
756 
757 		ArrayList getChildren( String name )
758 		{
759 			return (ArrayList) children.get( name );
760 		}
761 				
762 	}
763 
764 }
765 
766 
767 
768 
769