View Javadoc

1   /*
2    * $Id IndexGenerator.java $
3    *
4    */
5   
6   package org.astrogrid.fitsserver.setup;
7   import org.astrogrid.fitsserver.fits.*;
8   
9   
10  /***
11   * A standalone tool that generates an index based on the fits files it is
12   * is pointed at.
13   *
14   * @author M Hill
15   */
16  
17  import java.io.*;
18  
19  import java.net.HttpURLConnection;
20  import java.net.MalformedURLException;
21  import java.net.URL;
22  import java.text.DateFormat;
23  import java.text.ParseException;
24  import java.text.SimpleDateFormat;
25  import java.util.Date;
26  import java.util.Calendar;
27  import java.util.Locale;
28  import javax.xml.parsers.ParserConfigurationException;
29  import nom.tam.fits.Fits;
30  import nom.tam.fits.FitsException;
31  import nom.tam.fits.Header;
32  import nom.tam.fits.HeaderCard;
33  import nom.tam.fits.BasicHDU;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.astrogrid.cfg.ConfigFactory;
37  import org.astrogrid.dataservice.queriers.QuerierPluginException;
38  import org.astrogrid.xml.DomHelper;
39  import org.w3c.dom.Document;
40  import org.xml.sax.SAXException;
41  
42  
43  import org.astrogrid.xmldb.client.QueryService;
44  import org.astrogrid.xmldb.client.XMLDBFactory;
45  
46  import org.xmldb.api.base.ResourceSet;
47  import org.xmldb.api.modules.XMLResource;
48  import org.xmldb.api.base.Resource;
49  import org.xmldb.api.base.Collection;
50  import org.xmldb.api.base.XMLDBException;
51  
52  
53  
54  public class IndexGenerator
55  {
56     /*** this is meant to run as a command-line program, so output should go to the
57      * console, but it uses routines that use commons-logging anyway...
58      */
59     public static Log log = LogFactory.getLog(IndexGenerator.class);
60     
61     /*** Should we test for extensions.  There is no standard way of checking if
62      * a FITS file has extensions (the EXTEND keyword merely says that it *may*
63      * have extensions), and for large FITS files where you *know* there are no
64      * extensions, this can take some time.  So set this to false */
65     public boolean checkForExtensions = true;
66  
67     /*** Configures which axis is the RA (if any) */
68     public int raAxis = -1;
69  
70     /*** Configures which axis is the DEC (if any) */
71     public int decAxis = -1;
72  
73     /*** Configures the format of dates in the fits file headers so they can be parsed*/
74     public DateFormat fitsDateFormat = DateFormat.getDateTimeInstance();
75  
76     /*** Defines the format of the dates in the index - although they will also be output in seconds */
77     public SimpleDateFormat indexDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
78  
79     /*** The root element */
80     public static final String ROOT_START = "<FitsIndex>";
81     public static final String ROOT_END = "</FitsIndex>";
82     
83     /***
84      * Generates a single index FitsFile 'snippet' for the FITS file at the
85      * given url
86      */
87     public String makeIndexSnippet(URL fitsUrl) throws IOException, FitsException
88     {
89        assert fitsUrl != null;
90        fileNameFromDate = null;
91        
92        log.info("Examining file "+fitsUrl+"...");
93        return makeIndexSnippet(new Fits(fitsUrl), fitsUrl.toString());
94     }
95     
96     /***
97      * Generates an index 'snippet' for the FITS file at the
98      * given url
99      * @todo tidy up so that, for example, multiline comments become one tag
100     * @TODO max pixels are WRONG
101     */
102    public String makeIndexSnippet(Fits reader, String filename) throws IOException, FitsException
103    {
104       StringBuffer snippet = new StringBuffer();
105       System.out.println("entered makeIndexNippet(fitsreader,file)");
106       System.out.println("Number of hdus = " + reader.getNumberOfHDUs());
107       System.out.println("Number of BAsicHDus again from read = " + reader.read().length);
108       for (int i = 0; i < reader.getNumberOfHDUs(); i++)
109       {
110          log.trace(""); //seperate headers
111          log.trace(""); //seperate header-reading trace code
112          Header header = reader.getHDU(i).getHeader();
113    
114          snippet.append(makeIndexSnippet(header, filename));
115       }
116 
117       return snippet.toString();
118    }
119    
120    /***
121     * Generates an index snippet for the given header.
122     * Could probably do with tidying this up a bit, for example so that multiline comments become one tag
123     * End of line comments are included as 'Comment' attributes within the keyword tag, so that they are searchable
124     */
125    public String makeIndexSnippet(Header header, String fileLocation) throws IOException, FitsException
126    {
127       System.out.println("entered makeIndexSnippet(header,filelocation)");
128       if(header == null) {
129         return "";
130       }
131       System.out.println("header not null");
132                                 
133       //run trhough all keywords adding them
134       StringBuffer keywordSnippet = new StringBuffer();
135       
136 //proper way, but the object returned by header.iterator().next() is not documented      Iterator keys = header.iterator();
137       for (int i = 0; i < header.getNumberOfCards(); i++)
138       {
139          String key = header.getKey(i).trim();
140          String value = header.getStringValue(key);
141          //if the key is 'comment' leave it at that, if the comment comes from and-of-line comment, include that too
142          String eolComment = null;
143          String commentAtt = "";
144          /*
145          if (!key.toUpperCase().equals("COMMENT")) {
146             eolComment =header.findCard(key).getComment().trim();
147             if (eolComment.length()==0) {
148                eolComment = null;
149             }
150          }
151          
152          if (eolComment != null) {
153             commentAtt = " comment='"+eolComment+"'";
154          }
155          */
156          
157          if (value == null || value.trim().length() <= 0) {
158             //keywordSnippet.append("      <"+key+commentAtt+"/>\n");
159              //keywordSnippet.append("      <"+key+"/>\n");
160          } else {
161             try {
162                //if it's a date, we store two key entries, one with a unit attribute set to 's' and the time in ms since/before 1970
163                //Date dateVal = fitsDateFormat.parse(value);
164                 Date dateVal = indexDateFormat.parse(value);
165                keywordSnippet.append("      <"+key+" unit='s'"+commentAtt+">"+dateVal.getTime()+"</"+key+">\n");
166                //keywordSnippet.append("      <"+key+ " unit='s'>"+dateVal.getTime()+"</"+key+">\n");
167                value = indexDateFormat.format(dateVal);
168                if(fileNameFromDate == null && key.equals("DATE_OBS")) {                   
169                    fileNameFromDate = value.replaceAll("[^//w*]","-");
170                    //System.out.println("found fileNaemfromdate setting it to = " + fileNameFromDate);
171                }
172             } catch (ParseException e) {
173                //ignore
174                 //System.out.println("3PARSE-Exception FOR KEY = " + key + " and value = " + value + " Error = " + e.toString());
175             }
176                 System.out.println("The key = " + key + " the value = " + value);
177                 if(fileNameFromDate == null && key.equals("DATE_OBS")) {
178                     fileNameFromDate = value.replaceAll("[^//w*]","-");
179                 }
180                 keywordSnippet.append("      <"+key+commentAtt+">"+value+"</"+key+">\n");
181             //keywordSnippet.append("      <"+key+">"+value+"</"+key+">\n");
182          }
183       }
184       
185       
186       //assemble snippet
187       String snippet = "<FitsFile>\n"+
188                "   <Filename>"+fileLocation+"</Filename>\n"+
189                makeCoverageSnippet(header) + "<Keywords>" +
190                keywordSnippet.toString() + "</Keywords>" +
191                "</FitsFile>\n";
192       
193       validate(snippet); //debug - test snippet
194       
195       return snippet;
196    }
197 
198    /***
199     * Makes a Coverage snippet from the keywords in the given header, depending
200     * on the raAxis, decAxis settings */
201    private String makeCoverageSnippet(Header header) {
202       if ((header.getIntValue("NAXIS") >= 2) && (raAxis != -1) && (decAxis != -1)) {
203           System.out.println("the invalid for naxis = " + header.getIntValue("NAXIS") + " raaxis = " + raAxis + " decAxis = " + decAxis);
204          //return "";
205       
206          //work out coverage.  This is not a straightforward rectangle, as the
207          //image may be rotated and possibly even skewed.  So just give it as a
208          //polygon with 4 points.
209          FitsWCS wcsCalculator = new FitsWCS(header);
210          double maxPixelRA  = header.getDoubleValue("NAXIS"+raAxis);
211          double maxPixelDec = header.getDoubleValue("NAXIS"+decAxis);
212    
213          double[] point1 = wcsCalculator.toWCS( new double[] {0,0});
214          double[] point2 = wcsCalculator.toWCS( new double[] {0,maxPixelDec});
215          double[] point3 = wcsCalculator.toWCS( new double[] {maxPixelRA,maxPixelDec});
216          double[] point4 = wcsCalculator.toWCS( new double[] {maxPixelRA,0});
217          
218          //NB origin pixels might not be the min RA & DEC - the picture might be 'upside down'
219          return
220             "   <Coverage shape='Polygon'>\n"+
221             "      <Point><RA>"+point1[0]+"</RA><Dec>"+point1[1]+"</Dec></Point>\n"+
222             "      <Point><RA>"+point2[0]+"</RA><Dec>"+point2[1]+"</Dec></Point>\n"+
223             "      <Point><RA>"+point3[0]+"</RA><Dec>"+point3[1]+"</Dec></Point>\n"+
224             "      <Point><RA>"+point4[0]+"</RA><Dec>"+point4[1]+"</Dec></Point>\n"+
225             "   </Coverage>\n";
226       }
227       return "";
228    }
229    
230    /*** Checks that the given snippet is valid XML */
231    public static void validate(String snippet) throws IOException
232    {
233      try
234      {
235         DomHelper.newDocument(new ByteArrayInputStream(snippet.getBytes()));
236      }
237      catch (org.xml.sax.SAXException e) { throw new IOException(e.toString()); }
238       
239    }
240    
241    private static File fileIndexDir = null;
242    private File getIndexDirectory() {
243        String indexPath = String.valueOf(System.currentTimeMillis());
244        String indexGenHomePath = ConfigFactory.getCommonConfig().getString("indexgen.path", ("." + File.separator) );
245        if(!indexGenHomePath.endsWith(File.separator))
246            indexGenHomePath += File.separator;
247        fileIndexDir = new File(indexGenHomePath + indexPath);
248        if(!fileIndexDir.exists())
249            fileIndexDir.mkdir();
250        return fileIndexDir;
251    }
252 
253    
254    /***
255     * Generates an index XML file for the FITS files at the URLs listed in the
256     * given file, writing them out to the target stream
257     */
258    public File generateIndex(InputStream urlsIn) throws IOException, FitsException
259    {
260       System.out.println("entered generateIndex(inputstream)");
261       BufferedReader in = new BufferedReader(new InputStreamReader(urlsIn));
262       String line = null;
263       while( (line = in.readLine()) != null) {
264             generateIndex(new URL(line));
265             seqNum++;
266       }
267       return fileIndexDir;
268    }
269    
270    /***
271     * Generates an index XML file for the FITS files at the URLs listed in the
272     * given file, writing them out to the target stream
273     */
274    public File generateIndex(URL []urls) throws IOException, FitsException
275    {
276       System.out.println("entered generateIndex(urls[])");
277       for(int i = 0; i < urls.length;i++) {
278             generateIndex(urls[i]);
279             seqNum++;
280       }//for
281       return fileIndexDir;
282    }
283    
284    long seqNum = 0;
285    private String fileNameFromDate = null;
286 
287    /***
288     * Generates an index XML file for the FITS files at the given URLs, writing it out to the target stream
289     */
290    public File generateIndex(URL url) throws IOException, FitsException
291    {
292        System.out.println("entered generateIndex(url)");
293        if(url == null)
294            System.out.println("the url is null");
295        Calendar rightNow = Calendar.getInstance();
296        int year = rightNow.get(Calendar.YEAR);
297        int month = rightNow.get(Calendar.MONTH);
298        int day = rightNow.get(Calendar.DATE);
299        String dirPath = String.valueOf(year) + "_" + String.valueOf(month) + "_" + String.valueOf(day);
300        
301        if(fileIndexDir == null)
302            getIndexDirectory();
303        File xmlFile = null;
304        FileWriter fw = null;
305        PrintWriter pw = null;
306        //long seqNum = System.currentTimeMillis();
307        String xmlSnippet = null;
308           try {
309              xmlSnippet = "<FitsIndex>" + makeIndexSnippet(url) + "</FitsIndex>";
310              String fileName = null;
311              if(fileNameFromDate != null) {
312                  fileName = fileNameFromDate + "_" + String.valueOf((seqNum)) + ".xml";
313              }else {
314                  fileName = dirPath + "_" + String.valueOf((seqNum)) + ".xml";
315              }                 
316              System.out.println("the xmlsnippet = " + xmlSnippet);
317              xmlFile = new File(fileIndexDir,fileName);
318              fw = new FileWriter(xmlFile);
319              pw = new PrintWriter(fw);
320              try {
321                  DomHelper.DocumentToWriter(DomHelper.newDocument(xmlSnippet),pw);
322              }catch(Exception e) {
323                  e.printStackTrace();
324              }
325              pw.flush();
326              fw.close();
327              pw.close();
328           } catch (IOException ioe) {
329              //log as an error but try next URL
330              log.error(ioe+", processing URL "+url.toString(), ioe);
331           }
332           return fileIndexDir;
333    }
334    
335    private void updateXMLDB(String directoryName) throws Exception {
336        File fi = new File(directoryName);
337        if(!fi.exists()) {
338            String indexGenHomePath = ConfigFactory.getCommonConfig().getString("indexgen.path", ("." + File.separator));
339            if(!indexGenHomePath.endsWith(File.separator))
340                indexGenHomePath += File.separator;
341            fi = new File(indexGenHomePath + directoryName);
342            if(!fi.exists()) {
343                System.out.println("The directory or path does not seem to exist: directory=" + directoryName +
344                        "or " + indexGenHomePath + directoryName);
345                System.exit(1);
346            }//if
347        }
348        if(!fi.isDirectory()) {
349            System.out.println("There is a file that exists here, but is reported to not be a directory.  Now exiting");
350            System.exit(1);
351        }
352        updateXMLDB(fi);
353    }
354    
355    public void updateXMLDB(File fi) throws Exception {
356        
357        String line = null;
358        String correct = "N";
359        String uploadCollection = ConfigFactory.getCommonConfig().getString("upload.collection", null);
360        String adminUser = ConfigFactory.getCommonConfig().getString("xmldb.admin.user", "admin");
361        String adminPass = ConfigFactory.getCommonConfig().getString("xmldb.admin.password", "");
362        String xmldbURI = ConfigFactory.getCommonConfig().getString("xmldb.uri", "xmldb:exist://");
363        String xmldbConfig = ConfigFactory.getCommonConfig().getString("exist.config.file", "../exist.xml");
364        String xmldbDriver = ConfigFactory.getCommonConfig().getString("xmldb.driver", "org.exist.xmldb.DatabaseImpl");
365        String testBypass = ConfigFactory.getCommonConfig().getString("test.bypass", "no");
366        String fileNamePattern = "1*//.xml";
367 
368        if(testBypass.equals("no")) {
369            InputStreamReader consoleReader = new InputStreamReader(System.in);
370            BufferedReader consoleInput = new BufferedReader(consoleReader);
371            System.out.println("What is the Table Name (Collection Name) you wish to put in the xml files into. Example: TraceData, EITData");
372            System.out.println("You may also do sub-tables (sub-collections) with a '/' such as: CDSData/CDS2001, Soho/CDSData, MSSL/Soho/CDSData, RAL/Soho/Tape/CDSData");
373            while(!correct.equals("Y")) {
374                while( (line = consoleInput.readLine()) != null) ;
375                line = line.replaceAll("[^//w*]","_");
376                uploadCollection = line;
377                System.out.println("The full location of placing xml files will be: (You cannot get rid of /db/dcfitsfiles");
378                System.out.println("/db/dcfitsfiles/" + uploadCollection);
379                System.out.println("Is this all correct Y|N");
380                while( (correct = consoleInput.readLine()) != null) ;
381                correct = correct.toUpperCase();
382            }
383            
384            System.out.println("What is the file name pattern you wish to upload? ex: 2002*, *.xml,");
385            while( (line = consoleInput.readLine()) != null) ;
386            fileNamePattern = line;
387            
388            correct = "N";
389            
390     
391            int changeNum = -1;
392            while(!correct.equals("Y")) {
393                System.out.println("Do you need to correct|change any of the below settings, Normally No if running pal with internal eXist db. [Y|N]");
394                System.out.println("Commonly number 1 may be changed, and at times 4");
395                System.out.println("1.) xmldb.uri = URI to the database. Default: " + xmldbURI + " (external example: xmldb:exist://localhost:9080/exist/xmlrpc)");
396                System.out.println("2.) xmldb.admin.user = Administration user for adding xml files. Default: " + adminUser);
397                System.out.println("3.) xmldb.admin.password = Administration password for adding xml files. Default: " + adminPass);
398                System.out.println("4.) xmldb.configuration = Location of the xml db configuration file; If your running eXist internally advise to change read pal configuration page. Default: " + xmldbConfig);
399                System.out.println("5.) xmldb.driver = The XML database driver. Default: " + xmldbDriver);
400                while( (line = consoleInput.readLine()) != null) ;
401                correct = correct.toUpperCase();
402                if(correct.equals("N")) {
403                    System.out.println("Which number would you like to change [1-5], or 0 to exit changing");
404                    while(changeNum < 0) {
405                        while( (line = consoleInput.readLine()) != null) ;
406                        changeNum = Integer.parseInt(line);
407                        if(changeNum > 5 || changeNum < 0) {
408                            System.out.println("Invalid Number");
409                            changeNum = -1;
410                        }
411                        if(changeNum == 1)
412                            xmldbURI = askQuestion("What xmldb.uri do you wish to use?");
413                        else if(changeNum == 2)
414                            adminUser = askQuestion("What is the admin user?");
415                        else if(changeNum == 3)
416                            adminPass = askQuestion("What is the admin password?");
417                        else if(changeNum == 4)
418                            xmldbConfig = askQuestion("What is the location of the xmldb configuration file 'exist.xml'?");
419                        else if(changeNum == 5)
420                            xmldbDriver = askQuestion("What is the XMLDB driver?");
421                    }//while
422                }//if
423            }//while
424        }//bypassif
425        
426 
427        ConfigFactory.getCommonConfig().setProperty("xmldb.uri", xmldbURI);
428        ConfigFactory.getCommonConfig().setProperty("xmldb.driver", xmldbDriver);
429        ConfigFactory.getCommonConfig().setProperty("xmldb.admin.user", adminUser);
430        ConfigFactory.getCommonConfig().setProperty("xmldb.admin.password", adminPass);
431        //commented out until latest dependency arrives
432        if(!dbRegistered) {
433            XMLDBFactory.registerDB(xmldbConfig);
434            dbRegistered = true;
435        }
436        
437        XMLDBFactory xdb = new XMLDBFactory();
438        System.out.println("Now Registering the database");
439        
440        File []xmlFiles = fi.listFiles();
441        Document xmlDoc = null;
442        Collection coll = null;
443        try {
444            coll = xdb.openAdminCollection(("dcfitsfiles/" + uploadCollection));
445            
446            for(int i = 0;i < xmlFiles.length;i++) {
447                //System.out.println("the getName for the file = " + xmlFiles[i].getName() + " the file pattern = " + fileNamePattern);
448                //if(xmlFiles[i].getName().matches(fileNamePattern)) {
449                //    System.out.println("yes it matched");
450                    xmlDoc = DomHelper.newDocument(xmlFiles[i]);
451                    xdb.storeXMLResource(coll,xmlFiles[i].getName(),xmlDoc);
452                //}
453            }//for
454        }finally {
455            if(coll != null) {
456                xdb.closeCollection(coll);
457            }//if
458        }
459    }
460    
461    private static boolean dbRegistered = false;
462    
463    private static String askQuestion(String question) throws Exception {
464        InputStreamReader consoleReader = new InputStreamReader(System.in);
465        BufferedReader consoleInput = new BufferedReader(consoleReader);
466        String line = null;
467        while( (line = consoleInput.readLine()) != null) ;
468        return line;
469    }
470    
471    
472    /***
473      * Test harness
474      */
475     public static void main(String args[]) throws IOException, MalformedURLException, FitsException, Exception
476     {
477       //this is a bit of a botch so that if there's no log4j.properties around, it
478       //makes one
479       IndexGenerator generator = new IndexGenerator();
480       
481       Locale.setDefault(Locale.UK);
482       Document indexDoc = null;
483       if(args == null || args.length < 2) {
484          System.out.println("java IndexGenerator -file <filename of urls (one url per line)> or");
485          System.out.println("java IndexGenerator -url <single url>");
486          System.out.println("java IndexGenerator -update <directory of xml files>");
487          return;
488       }
489 
490           if("-file".equals(args[0])) {
491              generator.generateIndex(new FileInputStream(args[1]));
492           }else if("-url".equals(args[0])) {
493              String urlString = args[1];
494              generator.generateIndex(new URL(urlString));
495           }else if("-update".equals(args[0])) {
496               generator.updateXMLDB(args[1]);
497           }
498     }
499 
500 }
501 
502 /*
503 $Log: IndexGenerator.java,v $
504 Revision 1.8  2005/03/23 10:04:24  KevinBenson
505 small change to generate a particular file with date.
506 
507 Revision 1.7  2005/03/21 18:45:55  mch
508 Naughty big lump of changes
509 
510 Revision 1.6  2005/03/14 16:09:31  KevinBenson
511 Fixed up some more tests for the IndexGenerator
512 
513 Revision 1.5  2005/03/14 15:07:52  KevinBenson
514 now the test works and writes to a xml file
515 
516 Revision 1.4  2005/03/13 11:43:44  KevinBenson
517 small change for the indextenerator to create directories under target
518 
519 Revision 1.3  2005/03/11 16:19:58  KevinBenson
520 new indexgenerator worked around.
521 
522 Revision 1.2  2005/03/11 14:50:59  KevinBenson
523 added catch for parserconfigurationexception
524 
525 Revision 1.1  2005/03/10 16:42:55  mch
526 Split fits, sql and xdb
527 
528 Revision 1.3  2005/03/10 13:59:00  KevinBenson
529 corrections to fits and testing to fits
530 
531 Revision 1.2  2005/03/01 15:58:34  mch
532 Changed to use starlinks tamfits library
533 
534 Revision 1.1.1.1  2005/02/17 18:37:35  mch
535 Initial checkin
536 
537 Revision 1.1.1.1  2005/02/16 17:11:24  mch
538 Initial checkin
539 
540 Revision 1.3  2004/11/09 17:42:22  mch
541 Fixes to tests after fixes for demos, incl adding closable to targetIndicators
542 
543 Revision 1.2  2004/10/18 13:11:30  mch
544 Lumpy Merge
545 
546 Revision 1.1.6.1  2004/10/15 19:59:06  mch
547 Lots of changes during trip to CDS to improve int test pass rate
548 
549 Revision 1.1  2004/09/28 15:02:13  mch
550 Merged PAL and server packages
551 
552 Revision 1.21  2004/09/08 15:45:33  mch
553 Fix for unconfigured ra/dec axis
554 
555 Revision 1.20  2004/09/07 14:52:10  mch
556 Fixes etc for SEC
557 
558 Revision 1.19  2004/09/07 13:50:43  mch
559 Added fix so generator continues even if there's a problem reading a file
560 
561 Revision 1.18  2004/09/07 09:48:34  mch
562 Logging updates
563 
564 Revision 1.17  2004/09/07 01:39:27  mch
565 Moved email keys from TargetIndicator to Slinger
566 
567 Revision 1.16  2004/09/07 00:54:20  mch
568 Tidied up Querier/Plugin/Results, and removed deprecated SPI-visitor-SQL-translator
569 
570 Revision 1.15  2004/09/06 21:38:34  mch
571 Changed to take InputStream
572 
573 Revision 1.14  2004/09/06 21:20:01  mch
574 Factored out generateIndex for a filename for tests
575 
576 Revision 1.13  2004/08/05 15:14:22  KevinBenson
577 small bug fix in the FitsREsults.  And now uses dates was teh result of the mber of kevin-dev-03-08-04
578 
579 Revision 1.11.2.1  2004/08/05 15:10:35  KevinBenson
580 Changes to look for dates and make dates into UTC dates.
581 
582 Revision 1.11  2004/07/29 11:17:44  KevinBenson
583 small change to check that the header is null return an empty string
584 
585 Revision 1.10  2004/07/29 11:15:22  KevinBenson
586 *** empty log message ***
587 
588 Revision 1.9  2004/07/29 11:05:45  KevinBenson
589 small change to make sure the header is not null
590 
591 Revision 1.8  2004/07/26 13:53:44  KevinBenson
592 Changes to Fits to do an xquery on an xml file dealing with fits data.
593 Small xsl style sheet to make the xql which will get the filename element
594 
595 Revision 1.7.4.2  2004/07/26 08:53:40  KevinBenson
596 Still need to make a few more corrections, but wanted to check this in now.
597 It is the fits querier that now uses exist for doing adql->xquery
598 
599 Revision 1.7.4.1  2004/07/16 09:02:25  KevinBenson
600 small change for IndexGenerator to use arguments for it's files and urls.  And
601 Fitskeyword to handle dates.
602 
603 Revision 1.7  2004/07/12 23:26:14  mch
604 Botch fix for no extensions
605 
606 Revision 1.6  2004/03/12 04:45:26  mch
607 It05 MCH Refactor
608 
609 Revision 1.5  2004/03/08 00:31:28  mch
610 Split out webservice implementations for versioning
611 
612 Revision 1.4  2004/01/13 00:33:14  nw
613 Merged in branch providing
614 * sql pass-through
615 * replace Certification by User
616 * Rename _query as Query
617 
618 Revision 1.3.4.1  2004/01/08 09:43:41  nw
619 replaced adql front end with a generalized front end that accepts
620 a range of query languages (pass-thru sql at the moment)
621 
622 Revision 1.3  2003/12/15 14:34:02  mch
623 Added trace code, extra fits file
624 
625 Revision 1.2  2003/11/28 19:57:15  mch
626 Cone Search now works
627 
628 Revision 1.1  2003/11/28 18:22:18  mch
629 IndexGenerator now working
630 
631 */
632 
633 
634