001    /*
002     * Copyright 2001,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.apache.commons.jjar;
018    
019    import java.util.Properties;
020    import java.util.StringTokenizer;
021    import java.util.ArrayList;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Stack;
025    import java.util.Map;
026    import java.util.HashMap;
027    
028    import java.io.StringWriter;
029    import java.io.Writer;
030    import java.io.InputStream;
031    import java.io.InputStreamReader;
032    import java.io.BufferedInputStream;
033    import java.io.IOException;
034    
035    import java.net.URL;
036    import java.net.URLConnection;
037    
038    import org.xml.sax.*;
039    import uk.co.wilson.xml.MinML2;
040    
041    /**
042     *  Repository implementation that supports XML repository
043     *  definitions.  Uses a great small little lightweight SAX
044     *  parser - but it's not clear what it doesn't support, so
045     *  be careful
046     *
047     *  @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
048     *  @version $Id: RepositoryXML.java 155454 2005-02-26 13:23:34Z dirkv $
049     */
050    public class RepositoryXML extends MinML2 implements Repository
051    {
052            /** list of packages */
053        private HashMap packageMap = new HashMap();
054            int packageCount = 0;
055    
056            /** handles package dependency generation */
057            private static DependencyEngine de = new DependencyEngine();
058    
059            /** root of our document tree */
060        private Node root = null;
061    
062            /** stack used for SAX parsing */
063        private Stack  stack = new Stack();
064     
065            /** user for SAX parsing to avoid character events */
066        private StringWriter saxWriter = null;
067    
068        /** string constants for use in data structure */
069            private static String PACKAGENAME = "packagename";
070            private static String DEFAULTVERSION = "defaultversion";
071            private static String NODE = "node";
072            private static String DESCRIPTION = "description";
073            private static String HREF = "href";
074            private static String VERSION = "version";  
075        private static String VERSION_INFO = "version_info";
076        private static String VERSION_JAR = "version_jar";
077        private static String DEP_PACKAGE = "dep_package";
078        private static String DEP_VERSION = "dep_version";
079        private static String VERSION_DEPS = "version_deps";
080        private static String VERSION_ARRAY = "version_array";
081     
082            /**
083             *  loads the object with the XML data from the
084             *  passed in URL.  Invokes the SAX parser.
085             *
086             *  @param url location of the repository.xml
087             */
088        public void load( URL url )
089        {
090            load( url, packageMap );
091        }
092    
093        public void load( URL url, Map pkgMap )
094        {
095            try 
096            {
097                URLConnection conn = url.openConnection();
098                InputStream is = conn.getInputStream();
099    
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