ee1e3decd03078aefa6eed8b28b3b29ad3ddde24
[debian/jabref.git] / src / java / net / sf / jabref / wizard / auximport / AuxSubGenerator.java
1 /*
2 Copyright (C) 2004 R. Nagel
3
4 All programs in this directory and
5 subdirectories are published under the GNU General Public License as
6 described below.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or (at
11 your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA
22
23 Further information about the GNU GPL is available at:
24 http://www.gnu.org/copyleft/gpl.ja.html
25
26 */
27
28 /**
29  * <p>Title: Latex Aux to Bibtex</p>
30  *
31  * <p>Description: generates a sub-database which contains only bibtex entries
32  * from input aux file</p>
33  *
34  * <p>Copyright: Copyright (c) 2004</p>
35  *
36  * <p>Company: </p>
37  *
38  * @version 1.0
39  * @author r.nagel
40  *
41  * @todo Redesign of dialog structure for an assitent like feeling....
42  *   Now - the unknown bibtex entries cannot inserted into the reference
43  *   database without closing the dialog.
44  */
45
46 // created by : r.nagel 23.08.2004
47 //
48 // modified : - 11.04.2005
49 //              handling \\@input{file.aux} tag in aux files (nested aux files)
50
51
52 package net.sf.jabref.wizard.auximport ;
53
54 import java.io.BufferedReader;
55 import java.io.File;
56 import java.io.FileNotFoundException;
57 import java.io.FileReader;
58 import java.io.IOException;
59 import java.util.HashSet;
60 import java.util.Iterator;
61 import java.util.Vector;
62 import java.util.regex.Matcher;
63 import java.util.regex.Pattern;
64
65 import net.sf.jabref.BibtexDatabase;
66 import net.sf.jabref.BibtexEntry;
67 import net.sf.jabref.KeyCollisionException;
68 import net.sf.jabref.Util;
69
70 public class AuxSubGenerator
71 {
72
73   private HashSet<String> mySet ; // all unique bibtex keys in aux file
74
75   private Vector<String> notFoundList ; // all not solved bibtex keys
76
77   private BibtexDatabase db ; // reference database
78   private BibtexDatabase auxDB ; // contains only the bibtex keys who found in aux file
79
80   private int nestedAuxCounter ;  // counts the nested aux files
81   private int crossreferencedEntriesCount = 0; // counts entries pulled in due to crossref
82
83   public AuxSubGenerator(BibtexDatabase refDBase)
84   {
85     mySet = new HashSet<String>(20) ;
86     notFoundList = new Vector<String>() ;
87     db = refDBase ;
88   }
89
90   public final void setReferenceDatabase(BibtexDatabase newRefDB)
91   {
92     db = newRefDB ;
93   }
94
95   /**
96    * parseAuxFile
97    * read the Aux file and fill up some intern data structures.
98    * Nested aux files (latex \\include) supported!
99    *
100    * @param filename String : Path to LatexAuxFile
101    * @return boolean, true = no error occurs
102    */
103
104   // found at comp.text.tex
105   //  > Can anyone tell be the information held within a .aux file?  Is there a
106   //  > specific format to this file?
107   //
108   // I don't think there is a particular format. Every package, class
109   // or document can write to the aux file. The aux file consists of LaTeX macros
110   // and is read at the \begin{document} and again at the \end{document}.
111   //
112   // It usually contains information about existing labels
113   //  \\newlabel{sec:Intro}{{1}{1}}
114   // and citations
115   //  \citation{hiri:conv:1993}
116   // and macros to write information to other files (like toc, lof or lot files)
117   //  \@writefile{toc}{\contentsline {section}{\numberline
118   // {1}Intro}{1}}
119   // but as I said, there can be a lot more
120
121   // aux file :
122   //
123   // \\citation{x}  x = used reference of bibtex library entry
124   //
125   // \\@input{x}  x = nested aux file
126   //
127   // the \\bibdata{x} directive contains information about the
128   // bibtex library file -> x = name of bib file
129   //
130   // \\bibcite{x}{y}
131   //   x is a label for an item and y is the index in bibliography
132
133   public final boolean parseAuxFile(String filename)
134   {
135     // regular expressions
136     Pattern pattern ;
137     Matcher matcher ;
138
139     // while condition
140     boolean weiter = false ;
141
142     // return value -> default: no error
143     boolean back = true ;
144
145     // fileopen status
146     boolean loopFileOpen = false ;
147
148     // the important tag
149     pattern = Pattern.compile( "\\\\citation\\{.+\\}" ) ;
150
151     // input-file-buffer
152     BufferedReader br = null ;
153
154     // filelist, used for nested aux files
155     Vector<String> fileList = new Vector<String>(5) ;
156     fileList.add( filename );
157
158     // get the file path
159     File dummy = new File( filename ) ;
160     String path = dummy.getParent() ;
161     if (path != null)
162       path = path + File.separator ;
163     else
164       path = "" ;
165
166     nestedAuxCounter = -1 ;  // count only the nested reads
167
168     // index of current file in list
169     int fileIndex = 0 ;
170
171     while (fileIndex < fileList.size())
172     {
173       String fName = fileList.elementAt( fileIndex ) ;
174       try
175       {
176 //        System.out.println("read #"+fName +"#") ;
177         br = new BufferedReader( new FileReader( fName ) ) ;
178         weiter = true ;
179         loopFileOpen = true ;
180       }
181       catch ( FileNotFoundException fnfe )
182       {
183         System.out.println( "Cannot locate input file! " + fnfe.getMessage() ) ;
184         // System.exit( 0 ) ;
185         back = false ;
186         weiter = false ;
187         loopFileOpen = false ;
188       }
189
190       while ( weiter )
191       {
192         String line ;
193         try
194         {
195             if (br == null)
196                 throw new IOException();
197             line = br.readLine() ;
198         }
199         catch ( IOException ioe )
200         {
201           line = null ;
202           weiter = false ;
203         }
204
205         if ( line != null )
206         {
207           matcher = pattern.matcher( line ) ;
208
209           while ( matcher.find() )
210           {
211             // extract the bibtex-key(s) XXX from \citation{XXX} string
212             int len = matcher.end() - matcher.start() ;
213             if ( len > 11 )
214             {
215               String str = matcher.group().substring( matcher.start() + 10,
216                   matcher.end() - 1 ) ;
217               // could be an comma separated list of keys
218               String keys[] = str.split( "," ) ;
219               if ( keys != null )
220               {
221                 int keyCount = keys.length ;
222                 for ( int t = 0 ; t < keyCount ; t++ )
223                 {
224                   String dummyStr = keys[t] ;
225                   if ( dummyStr != null )
226                   {
227                     // delete all unnecessary blanks and save key into an set
228                     mySet.add( dummyStr.trim() ) ;
229 //                System.out.println("found " +str +" in AUX") ;
230                   }
231                 }
232               }
233             }
234           }
235           // try to find a nested aux file
236           int index = line.indexOf( "\\@input{" ) ;
237           if ( index >= 0 )
238           {
239             int start = index + 8 ;
240             int end = line.indexOf( "}", start ) ;
241             if ( end > start )
242             {
243               String str = path + line.substring( index + 8, end ) ;
244
245               // if filename already in filelist
246               if (!fileList.contains( str ) )
247               {
248                  fileList.add(str);   // insert file into filelist
249               }
250             }
251           }
252         } // line != null
253         else weiter = false ;
254       } // end of while
255
256       if ( loopFileOpen ) // only close, if open sucessful
257       {
258         try
259         {
260             if (br != null)
261                 br.close() ;
262           nestedAuxCounter++ ;
263         }
264         catch ( IOException ioe )
265         {}
266       }
267
268       fileIndex++ ; // load next file
269     }
270
271     return back ;
272   }
273
274   /**
275    * resolveTags
276    * Try to find an equivalent bibtex entry into reference database for all keys
277    * (found in aux file). This methode will fill up some intern data structures.....
278    */
279   public final void resolveTags()
280   {
281     auxDB = new BibtexDatabase() ;
282     notFoundList.clear();
283
284     Iterator<String> it = mySet.iterator() ;
285
286     // forall bibtex keys (found in aux-file) try to find an equivalent
287     // entry into reference database
288     while (it.hasNext())
289     {
290       String str = it.next() ;
291       BibtexEntry entry = db.getEntryByKey(str);
292
293       if (entry == null)
294       {
295         notFoundList.add(str) ;
296       } else
297       {
298           insertEntry(auxDB, entry);
299           // Check if the entry we just found references another entry which
300           // we don't already have in our list of entries to include. If so,
301           // pull in that entry as well:
302           String crossref = entry.getField("crossref");
303           if ((crossref != null) && (!mySet.contains(crossref))) {
304               BibtexEntry refEntry = db.getEntryByKey(crossref);
305               /**
306                * [ 1717849 ] Patch for aux import by Kai Eckert
307                */
308               if (refEntry == null) {
309                   notFoundList.add(crossref);
310               } else {
311                   insertEntry(auxDB, refEntry);
312                   crossreferencedEntriesCount++;
313               }
314           }
315
316       }
317     }
318   }
319
320     /**
321      * Insert a clone of the given entry. The clone is given a new unique ID.
322      * @param auxDB The database to insert into.
323      * @param entry The entry to insert a copy of.
324      */
325     private void insertEntry(BibtexDatabase auxDB, BibtexEntry entry) {
326         try {
327             BibtexEntry clonedEntry = (BibtexEntry)entry.clone();
328             clonedEntry.setId(Util.createNeutralId());
329             auxDB.insertEntry(clonedEntry);
330         } catch (KeyCollisionException e) {
331             e.printStackTrace();
332         }
333     }
334
335     /**
336      * generate
337      * Shortcut methode for easy generation.
338      *
339      * @param auxFileName String
340      * @param bibDB BibtexDatabase - reference database
341      * @return Vector - contains all not resolved bibtex entries
342      */
343     public final Vector<String> generate(String auxFileName, BibtexDatabase bibDB)
344     {
345       setReferenceDatabase(bibDB);
346       parseAuxFile(auxFileName) ;
347       resolveTags();
348
349       return notFoundList ;
350     }
351
352   public BibtexDatabase getGeneratedDatabase()
353   {
354     if (auxDB == null)
355       auxDB = new BibtexDatabase() ;
356
357     return auxDB ;
358   }
359
360   public final int getFoundKeysInAux()
361   {
362     return mySet.size() ;
363   }
364
365   public final int getResolvedKeysCount()
366   {
367     return auxDB.getEntryCount() - crossreferencedEntriesCount;
368   }
369
370   public final int getNotResolvedKeysCount()
371   {
372     return notFoundList.size() ;
373   }
374
375     /**
376      * Query the number of extra entries pulled in due to crossrefs from other
377      * entries.
378      * @return The number of additional entries pulled in due to crossref
379      */
380     public final int getCrossreferencedEntriesCount()
381     {
382         return crossreferencedEntriesCount;
383     }
384
385   /** reset all used datastructures */
386   public final void clear()
387   {
388     mySet.clear() ;
389     notFoundList.clear();
390     crossreferencedEntriesCount = 0;
391     // db = null ;  ???
392   }
393
394   /** returns a vector off all not resolved bibtex entries found in auxfile */
395   public Vector<String> getNotFoundList()
396   {
397     return notFoundList ;
398   }
399
400   /** returns the number of nested aux files, read by the last call of
401    *  generate method */
402   public int getNestedAuxCounter()
403   {
404     return this.nestedAuxCounter ;
405   }
406 }