/[classpath]/gjdoc/src/gnu/classpath/tools/gjdoc/RootDocImpl.java
ViewVC logotype

Diff of /gjdoc/src/gnu/classpath/tools/gjdoc/RootDocImpl.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.16 by julian, Mon Dec 13 22:00:59 2004 UTC revision 1.17 by julian, Mon Dec 20 21:27:52 2004 UTC
# Line 119  public class RootDocImpl Line 119  public class RootDocImpl
119    
120     private String sourceEncoding;     private String sourceEncoding;
121    
122       private Parser parser = new Parser();
123    
124       private Set unlocatableReportedSet = new HashSet();
125    
126       private Set inaccessibleReportedSet = new HashSet();
127      
128     //--------------------------------------------------------------------------     //--------------------------------------------------------------------------
129     //     //
130     // Implementation of RootDoc interface     // Implementation of RootDoc interface
# Line 189  public class RootDocImpl Line 195  public class RootDocImpl
195    
196     public void build() throws ParseException, IOException {     public void build() throws ParseException, IOException {
197    
       Parser parser = new Parser();  
   
198        //--- Create a temporary random access file for caching comment text.        //--- Create a temporary random access file for caching comment text.
199    
200        //File rawCommentCacheFile=File.createTempFile("gjdoc_rawcomment",".cache");        //File rawCommentCacheFile=File.createTempFile("gjdoc_rawcomment",".cache");
# Line 258  public class RootDocImpl Line 262  public class RootDocImpl
262    
263        loadScheduledClasses(parser);        loadScheduledClasses(parser);
264    
265          printNotice("Resolving references in comments...");
266    
267        resolveComments();        resolveComments();
268    
269        //--- Resolve pending references in all ClassDocImpls        //--- Resolve pending references in all ClassDocImpls
# Line 265  public class RootDocImpl Line 271  public class RootDocImpl
271        printNotice("Resolving references in classes...");        printNotice("Resolving references in classes...");
272    
273        for (Iterator it = classDocMap.values().iterator(); it.hasNext(); ) {        for (Iterator it = classDocMap.values().iterator(); it.hasNext(); ) {
274           ClassDocImpl cd=(ClassDocImpl)it.next();           ClassDoc cd=(ClassDoc)it.next();
275           cd.resolve();           if (cd instanceof ClassDocImpl) {
276                ((ClassDocImpl)cd).resolve();
277             }
278        }        }
279    
280        //--- Resolve pending references in all PackageDocImpls        //--- Resolve pending references in all PackageDocImpls
# Line 304  public class RootDocImpl Line 312  public class RootDocImpl
312        printNotice("Resolving references in class comments...");        printNotice("Resolving references in class comments...");
313    
314        for (Iterator it=classDocMap.values().iterator(); it.hasNext(); ) {        for (Iterator it=classDocMap.values().iterator(); it.hasNext(); ) {
315           ClassDocImpl cd=(ClassDocImpl)it.next();           ClassDoc cd=(ClassDoc)it.next();
316           cd.resolveComments();           if (cd instanceof ClassDocImpl) {
317                ((ClassDocImpl)cd).resolveComments();
318             }
319        }        }
320    
321        //--- Resolve pending references in comment data of all packages        //--- Resolve pending references in comment data of all packages
# Line 401  public class RootDocImpl Line 411  public class RootDocImpl
411        classDocMap.put(cd.qualifiedName(), cd);        classDocMap.put(cd.qualifiedName(), cd);
412     }     }
413    
414       public void addClassDocRecursive(ClassDoc cd) {
415          classDocMap.put(cd.qualifiedName(), cd);
416          ClassDoc[] innerClasses = cd.innerClasses(false);
417          for (int i=0; i<innerClasses.length; ++i) {
418             addClassDocRecursive(innerClasses[i]);
419          }
420       }
421    
422     public void addPackageDoc(PackageDoc pd) {     public void addPackageDoc(PackageDoc pd) {
423        packageDocMap.put(pd.name(), pd);        packageDocMap.put(pd.name(), pd);
424     }     }
# Line 430  public class RootDocImpl Line 448  public class RootDocImpl
448        if (classDocMap.get(qualifiedName)==null) {        if (classDocMap.get(qualifiedName)==null) {
449    
450           //Debug.log(9,"Scheduling "+qualifiedName+", context "+context+".");           //Debug.log(9,"Scheduling "+qualifiedName+", context "+context+".");
451             //System.err.println("Scheduling " + qualifiedName + ", context " + context);
452    
453           scheduledClasses.add(new ScheduledClass(context, qualifiedName));           scheduledClasses.add(new ScheduledClass(context, qualifiedName));
454        }        }
# Line 493  public class RootDocImpl Line 512  public class RootDocImpl
512    
513              try {              try {
514                 // Try to load the class                 // Try to load the class
515                   //printNotice("Trying to load " + scheduledClassName);
516                 loadScheduledClass(parser, scheduledClassName, scheduledClassContext);                 loadScheduledClass(parser, scheduledClassName, scheduledClassContext);
517              }              }
518              catch (ParseException e) {              catch (ParseException e) {
# Line 542  public class RootDocImpl Line 562  public class RootDocImpl
562    
563        if (loadedClass==null || loadedClass instanceof ClassDocProxy) {        if (loadedClass==null || loadedClass instanceof ClassDocProxy) {
564    
565           File file=findScheduledClassFile(scheduledClassName, scheduledClassContext);           ClassDoc classDoc = findScheduledClassFile(scheduledClassName, scheduledClassContext);
566           if (file!=null) {           if (null != classDoc) {
567              parser.processSourceFile(file, false, sourceEncoding, null);  
568           }              if (classDoc instanceof ClassDocReflectedImpl) {
569           else {                 if (classDoc.qualifiedName().indexOf("Point2D") >= 0) {
570                      System.err.println("adding class doc: " + classDoc);
571                   }
572                   Main.getRootDoc().addClassDocRecursive(classDoc);
573                }
574    
575                if (Main.DESCEND_SUPERCLASS
576                    && null != classDoc.superclass()
577                    && (classDoc.superclass() instanceof ClassDocProxy)) {
578                   scheduleClass(classDoc, classDoc.superclass().qualifiedName());
579                }
580             }
581             else {
582              // It might be an inner class of one of the outer/super classes.              // It might be an inner class of one of the outer/super classes.
583              // But we can only check that when they are all fully loaded.              // But we can only check that when they are all fully loaded.
584              boolean retryLater = false;              boolean retryLater = false;
# Line 596  public class RootDocImpl Line 628  public class RootDocImpl
628        }        }
629     }     }
630    
631       private static interface ResolvedImport
632       {
633          public String match(String name);
634          public boolean mismatch(String name);
635          public ClassDoc tryFetch(String name);
636       }
637    
638       private class ResolvedImportNotFound
639          implements ResolvedImport
640       {
641          private String importSpecifier;
642    
643     public File findScheduledClassFile(String scheduledClassName, ClassDoc scheduledClassContext)        ResolvedImportNotFound(String importSpecifier)
644          {
645             this.importSpecifier = importSpecifier;
646          }
647    
648        throws ParseException, IOException {        public String toString()
649          {
650             return "ResolvedImportNotFound{" + importSpecifier + "}";
651          }
652    
653        File rc;        public String match(String name)
654          {
655             return null; // FIXME!
656          }
657    
658        if (scheduledClassName.indexOf('.')<0) {        public boolean mismatch(String name)
659          {
660             return true; // FIXME!
661          }
662    
663           ClassDoc[] importedClasses=scheduledClassContext.importedClasses();        public ClassDoc tryFetch(String name)
664           int j;        {
665           for (j=0; j<importedClasses.length; ++j) {           return null;
666              if (importedClasses[j].qualifiedName().endsWith("."+scheduledClassName)) {        }
667                 rc = findClass(importedClasses[j].qualifiedName());     }
                if (rc!=null) return rc;  
             }  
          }  
               
          PackageDoc[] importedPackages=scheduledClassContext.importedPackages();  
668    
669           for (j=0; j<importedPackages.length; ++j) {     private class ResolvedImportPackageFile
670              rc = findClass(importedPackages[j].name()+"."+scheduledClassName);        implements ResolvedImport
671              if (rc!=null) return rc;     {
672           }        private Set topLevelClassNames;
673          private File packageFile;
674          private String packageName;
675          private Map cache = new HashMap();
676    
677          ResolvedImportPackageFile(File packageFile, String packageName)
678          {
679             this.packageFile = packageFile;
680             this.packageName = packageName;
681             topLevelClassNames = new HashSet();
682             File[] files = packageFile.listFiles();
683             for (int i=0; i<files.length; ++i) {
684                if (!files[i].isDirectory() && files[i].getName().endsWith(".java")) {
685                   String topLevelClassName = files[i].getName();
686                   topLevelClassName
687                      = topLevelClassName.substring(0, topLevelClassName.length() - 5);
688                   topLevelClassNames.add(topLevelClassName);
689                }
690             }
691          }
692    
693          public String match(String name)
694          {
695             ClassDoc loadedClass = classNamed(packageName + "." + name);
696             if (null != loadedClass) {
697                return loadedClass.qualifiedName();
698             }
699             else {
700                String topLevelName = name;
701                int ndx = topLevelName.indexOf('.');
702                String innerClassName = null;
703                if (ndx > 0) {
704                   innerClassName = topLevelName.substring(ndx + 1);
705                   topLevelName = topLevelName.substring(0, ndx);
706                }
707                
708                if (topLevelClassNames.contains(topLevelName)) {
709                   //System.err.println(this + ".match returns " + packageName + "." + name);
710                   return packageName + "." + name;
711                }
712                // FIXME: inner classes
713                else {
714                   return null;
715                }
716             }
717          }
718    
719          public boolean mismatch(String name)
720          {
721             return null == match(name);
722          }
723    
724          public ClassDoc tryFetch(String name)
725          {
726             ClassDoc loadedClass = classNamed(packageName + "." + name);
727             if (null != loadedClass) {
728                return loadedClass;
729             }
730             else if (null != match(name)) {
731    
732                String topLevelName = name;
733                int ndx = topLevelName.indexOf('.');
734                String innerClassName = null;
735                if (ndx > 0) {
736                   innerClassName = topLevelName.substring(ndx + 1);
737                   topLevelName = topLevelName.substring(0, ndx);
738                }
739    
740                ClassDoc topLevelClass = (ClassDoc)cache.get(topLevelName);
741                if (null == topLevelClass) {
742                   File classFile = new File(packageFile, topLevelName + ".java");
743                   try {
744                      // FIXME: inner classes
745                      topLevelClass = parser.processSourceFile(classFile, false, sourceEncoding, null);
746                   }
747                   catch (Exception ignore) {
748                      printWarning("Could not parse source file " + classFile);
749                   }
750                   cache.put(topLevelName, topLevelClass);
751                }
752                if (null == innerClassName) {
753                   return topLevelClass;
754                }
755                else {
756                   return getInnerClass(topLevelClass, innerClassName);
757                }
758             }
759             else {
760                return null;
761             }
762          }
763    
764          public String toString()
765          {
766             return "ResolvedImportPackageFile{" + packageFile + "," + packageName + "}";
767          }
768       }
769    
770       private ClassDoc getInnerClass(ClassDoc topLevelClass, String innerClassName)
771       {
772          StringTokenizer st = new StringTokenizer(innerClassName, ".");
773       outer:
774          
775          while (st.hasMoreTokens()) {
776             String innerClassNameComponent = st.nextToken();
777             ClassDoc[] innerClasses = topLevelClass.innerClasses();
778             for (int i=0; i<innerClasses.length; ++i) {
779                if (innerClasses[i].name().equals(innerClassNameComponent)) {
780                   topLevelClass = innerClasses[i];
781                   continue outer;
782                }
783             }
784             printWarning("Could not find inner class " + innerClassName + " in class " + topLevelClass.qualifiedName());
785             return null;
786          }
787          return topLevelClass;
788       }
789    
790       private class ResolvedImportClassFile
791          implements ResolvedImport
792       {
793          private File classFile;
794          private String innerClassName;
795          private String name;
796          private ClassDoc classDoc;
797          private boolean alreadyFetched;
798          private String qualifiedName;
799    
800          ResolvedImportClassFile(File classFile, String innerClassName, String name, String qualifiedName)
801          {
802             this.classFile = classFile;
803             this.innerClassName = innerClassName;
804             this.name = name;
805             this.qualifiedName = qualifiedName;
806          }
807    
808          public String toString()
809          {
810             return "ResolvedImportClassFile{" + classFile + "," + innerClassName +  "}";
811          }
812    
813          public String match(String name)
814          {
815             String topLevelName = name;
816             int ndx = topLevelName.indexOf('.');
817             String innerClassName = null;
818             if (ndx > 0) {
819                innerClassName = topLevelName.substring(ndx + 1);
820                topLevelName = topLevelName.substring(0, ndx);
821             }
822    
823             if (this.name.equals(topLevelName)) {
824                if (null == innerClassName) {
825                   return qualifiedName;
826                }
827                else {
828                   return qualifiedName + "." + innerClassName;
829                }
830             }
831             else {
832                return null;
833             }
834          }
835    
836          public boolean mismatch(String name)
837          {
838             return null == match(name);
839          }
840    
841          public ClassDoc tryFetch(String name)
842          {
843             if (null != match(name)) {
844                ClassDoc topLevelClass = null;
845                if (alreadyFetched) {
846                   topLevelClass = classDoc;
847                }
848                else {
849                   alreadyFetched = true;
850                   try {
851                      topLevelClass = parser.processSourceFile(classFile, false, sourceEncoding, null);
852                   }
853                   catch (Exception ignore) {
854                      printWarning("Could not parse source file " + classFile);
855                   }
856                }
857                if (null == topLevelClass) {
858                   return null;
859                }
860                else {
861                   return getInnerClass(topLevelClass, innerClassName);
862                }
863             }
864             else {
865                return null;
866             }
867          }
868    
869          public String getName()
870          {
871             if (innerClassName != null) {
872                return name + innerClassName;
873             }
874             else {
875                return name;
876             }
877          }
878       }
879      
880       private class ResolvedImportReflectionClass
881          implements ResolvedImport
882       {
883          private Class clazz;
884          private String name;
885    
886          ResolvedImportReflectionClass(Class clazz)
887          {
888             this.clazz = clazz;
889             String className = clazz.getName();
890             int ndx = className.lastIndexOf('.');
891             if (ndx >= 0) {
892                this.name = className.substring(ndx + 1);
893             }
894             else {
895                this.name = className;
896             }
897          }
898    
899          public String toString()
900          {
901             return "ResolvedImportReflectionClass{" + clazz.getName() + "}";
902          }
903    
904          public String match(String name)
905          {
906             if (this.name.equals(name)) {
907                return clazz.getName();
908             }
909             else {
910                return null;
911             }
912          }
913    
914          public boolean mismatch(String name)
915          {
916             return null == match(name);
917        }        }
918    
919        rc = findClass(scheduledClassName);        public ClassDoc tryFetch(String name)
920        if (rc!=null) return rc;        {
921             if (null != match(name)) {
922                return new ClassDocReflectedImpl(clazz);
923             }
924             // FIXME: inner classes?
925             else {
926                return null;
927             }
928          }
929    
930        if (scheduledClassContext.containingPackage()!=null) {        public String getName()
931           rc = findClass(scheduledClassContext.containingPackage().name()+"."+scheduledClassName);        {
932           if (rc!=null) return rc;           return name;
933        }        }
934       }
935    
936       private List unlocatablePrefixes = new LinkedList();
937    
938        return findClass("java.lang."+scheduledClassName);     private ResolvedImport resolveImport(String importSpecifier)
939       {
940          ResolvedImport result = resolveImportFileSystem(importSpecifier);
941          if (null == result) {
942             result = resolveImportReflection(importSpecifier);
943          }
944          if (null == result) {
945             result = new ResolvedImportNotFound(importSpecifier);
946          }
947          return result;
948     }     }
949    
950     public File findClass(String qualifiedName) throws IOException {     private ResolvedImport resolveImportReflection(String importSpecifier)
951       {
952          String importedPackageOrClass = importSpecifier;
953          if (importedPackageOrClass.endsWith(".*")) {
954             importedPackageOrClass = importedPackageOrClass.substring(0, importedPackageOrClass.length() - 2);
955             return null;
956          }
957          else {
958             try {
959                Class importedClass = Class.forName(importSpecifier);
960                return new ResolvedImportReflectionClass(importedClass);
961             }
962             catch (Throwable ignore) {
963                return null;
964             }
965          }
966       }
967    
968       private ResolvedImport resolveImportFileSystem(String importSpecifier)
969       {
970          for (Iterator it = unlocatablePrefixes.iterator(); it.hasNext(); ) {
971             String unlocatablePrefix = (String)it.next();
972             if (importSpecifier.startsWith(unlocatablePrefix)) {
973                return null;
974             }
975          }
976    
977        if (Main.getInstance().isDocletRunning()) return null;        String longestUnlocatablePrefix = "";
978    
979          for (Iterator it=sourcePath.iterator(); it.hasNext(); ) {
980                
981             File sourcePath = (File)it.next();
982    
983        if (Main.getRootDoc().getClassDoc(qualifiedName)==null) {           StringBuffer packageOrClassPrefix = new StringBuffer();
984             StringTokenizer st = new StringTokenizer(importSpecifier, ".");
985             while (st.hasMoreTokens() && sourcePath.isDirectory()) {
986                String token = st.nextToken();
987                if ("*".equals(token)) {
988                   return new ResolvedImportPackageFile(sourcePath,
989                                                        packageOrClassPrefix.substring(0, packageOrClassPrefix.length() - 1));
990                }
991                else {
992                   packageOrClassPrefix.append(token);
993                   packageOrClassPrefix.append('.');
994                   File classFile = new File(sourcePath, token + ".java");
995                   //System.err.println("  looking for file " + classFile);
996                   if (classFile.exists()) {
997                      StringBuffer innerClassName = new StringBuffer();
998                      while (st.hasMoreTokens()) {
999                         token = st.nextToken();
1000                         if (innerClassName.length() > 0) {
1001                            innerClassName.append('.');
1002                         }
1003                         innerClassName.append(token);
1004                      }
1005                      return new ResolvedImportClassFile(classFile, innerClassName.toString(), token, importSpecifier);
1006                   }
1007                   else {
1008                      sourcePath = new File(sourcePath, token);
1009                   }
1010                }
1011             }
1012             if (st.hasMoreTokens()) {
1013                if (packageOrClassPrefix.length() > longestUnlocatablePrefix.length()) {
1014                   longestUnlocatablePrefix = packageOrClassPrefix.toString();
1015                }
1016             }
1017          }
1018    
1019           String relPath=qualifiedName.replace('.',File.separatorChar)+".java";        if (longestUnlocatablePrefix.length() > 0) {
1020           //String filename=new File(Main.getRootDoc().getsourcePath()).getCanonicalFile().getAbsolutePath()+File.separatorChar+relPath;           System.err.println("adding unlocatablePrefix: " + longestUnlocatablePrefix);
1021           for (Iterator it=sourcePath.iterator(); it.hasNext(); ) {           unlocatablePrefixes.add(longestUnlocatablePrefix);
1022          }
1023    
1024              File sourcePath = (File)it.next();        return null;
1025       }
1026    
1027              String filename=sourcePath.getAbsolutePath()+File.separatorChar+relPath;     private Map resolvedImportCache = new HashMap();
             File file = null;  
1028    
1029              Debug.log(9, "loadClass: trying file "+filename);     private ResolvedImport getResolvedImport(String importSpecifier)
1030       {
1031          ResolvedImport result
1032             = (ResolvedImport)resolvedImportCache.get(importSpecifier);
1033          if (null == result) {
1034             result = resolveImport(importSpecifier);
1035             resolvedImportCache.put(importSpecifier, result);
1036          }
1037          return result;
1038       }
1039    
1040              // FIXME: the following can probably be done simpler and more elegant using File.getParent()     public String resolveClassName(String className, ClassDocImpl context)
1041       {
1042          Iterator it = context.getImportSpecifierList().iterator();
1043          while (it.hasNext()) {
1044             String importSpecifier = (String)it.next();
1045             ResolvedImport resolvedImport = getResolvedImport(importSpecifier);
1046             String resolvedScheduledClassName = resolvedImport.match(className);
1047             if (null != resolvedScheduledClassName) {
1048                return resolvedScheduledClassName;
1049             }
1050          }
1051          return className;
1052       }
1053    
1054              while ((!(file=new File(filename)).exists())     public ClassDoc findScheduledClassFile(String scheduledClassName,
1055                     || (!file.getAbsolutePath().toLowerCase().endsWith(relPath.toLowerCase()))) {                                            ClassDoc scheduledClassContext)
1056                 int ndx=filename.lastIndexOf(File.separatorChar);        throws ParseException, IOException
1057                 if (ndx>=0) {     {
1058                    filename=filename.substring(0,ndx)+".java";        String resolvedScheduledClassName = null;
                   //Debug.log(6,"loadClass: trying file "+filename);  
                }  
                else {  
                   file = null;  
                   break;  
                }  
             }  
1059    
1060              if (null!=file) return file;        if (scheduledClassContext instanceof ClassDocImpl) {
          }  
1061    
1062           return null;           //((ClassDocImpl)scheduledClassContext).resolveReferencedName(scheduledClassName);
1063             Iterator it = ((ClassDocImpl)scheduledClassContext).getImportSpecifierList().iterator();
1064             while (it.hasNext()) {
1065                String importSpecifier = (String)it.next();
1066                ResolvedImport resolvedImport = getResolvedImport(importSpecifier);
1067                //System.err.println("  looking in import '" +  resolvedImport + "'");
1068                resolvedScheduledClassName = resolvedImport.match(scheduledClassName);
1069                if (null != resolvedScheduledClassName) {
1070                   ClassDoc result = resolvedImport.tryFetch(scheduledClassName);
1071                   if (null != result) {
1072                      return result;
1073                   }
1074                   else {
1075                      if (!inaccessibleReportedSet.contains(scheduledClassName)) {
1076                         inaccessibleReportedSet.add(scheduledClassName);
1077                         printWarning("Error while loading class " + scheduledClassName);
1078                      }
1079                      // FIXME: output resolved class name here
1080                      return null;
1081                   }
1082                }
1083             }
1084        }        }
1085        else {        else {
1086           //Debug.log(9, "class \""+qualifiedName+"\" already loaded.");           System.err.println("findScheduledClassFile for '" + scheduledClassName + "' in proxy for " + scheduledClassContext);
          return null;  
1087        }        }
1088    
1089          // interpret as fully qualified name on file system
1090    
1091          ResolvedImport fqImport = resolveImportFileSystem(scheduledClassName);
1092          if (null != fqImport && fqImport instanceof ResolvedImportClassFile) {
1093             return fqImport.tryFetch(((ResolvedImportClassFile)fqImport).getName());
1094          }
1095    
1096          // use reflection, assume fully qualified class name
1097    
1098          if (!unlocatableReflectedClassNames.contains(scheduledClassName)) {
1099             try {
1100                Class clazz = Class.forName(scheduledClassName);
1101                printWarning("Cannot locate class " + scheduledClassName + " on file system, falling back to reflection.");
1102                ClassDoc result = new ClassDocReflectedImpl(clazz);
1103                return result;
1104             }
1105             catch (Throwable ignore) {
1106                unlocatableReflectedClassNames.add(scheduledClassName);
1107             }
1108          }
1109    
1110          if (null == resolvedScheduledClassName) {
1111             resolvedScheduledClassName = scheduledClassName;
1112          }
1113          if (!unlocatableReportedSet.contains(resolvedScheduledClassName)) {
1114             unlocatableReportedSet.add(resolvedScheduledClassName);
1115             printWarning("Cannot locate class " + resolvedScheduledClassName + " referenced in class " + scheduledClassContext.qualifiedName());
1116          }
1117          return null;
1118     }     }
1119    
1120       private Set unlocatableReflectedClassNames = new HashSet();
1121    
1122     public static boolean recursiveClasses = false;     public static boolean recursiveClasses = false;
1123    
1124     public void addSpecifiedPackageName(String packageName) {     public void addSpecifiedPackageName(String packageName) {
# Line 723  public class RootDocImpl Line 1166  public class RootDocImpl
1166        specifiedPackages = null;        specifiedPackages = null;
1167        scheduledClasses = null;        scheduledClasses = null;
1168        sourcePath = null;        sourcePath = null;
1169          parser = null;
1170          unlocatableReportedSet = null;
1171          inaccessibleReportedSet = null;
1172     }     }
1173    
1174     public void setSourceEncoding(String sourceEncoding)     public void setSourceEncoding(String sourceEncoding)
# Line 766  public class RootDocImpl Line 1212  public class RootDocImpl
1212        }        }
1213        return html.trim();        return html.trim();
1214     }     }
1215    
1216       public Parser getParser()
1217       {
1218          return parser;
1219       }
1220  }  }

Legend:
Removed from v.1.16  
changed lines
  Added in v.1.17

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26