001    package edu.nrao.sss.model.source;
002    
003    import java.util.ArrayList;
004    import java.util.Collections;
005    import java.util.Comparator;
006    import java.util.HashMap;
007    import java.util.List;
008    import java.util.Map;
009    
010    /**
011     * Structured representation of data from the old VLA Calibrator Manual.
012     * <p>
013     * <b>Warning:</b> This class is not expected to be used by anything other
014     * than the source catalog application under controlled circumstances.
015     * Because of this documentation herein is scanty and error handling has
016     * not been coded.  If you would like to use this class in your own
017     * environment, see the author about making this class more robust.</p>
018     * <p>
019     * <b>CVS Info:</b>
020     * <table style="margin-left:2em">
021     *   <tr><td>$Revision: 760 $</td></tr>
022     *   <tr><td>$Date: 2007-07-09 08:39:29 -0600 (Mon, 09 Jul 2007) $</td></tr>
023     *   <tr><td>$Author: dharland $</td></tr>
024     * </table></p>
025     * 
026     * @author David M. Harland
027     * @since 2006-12-13
028     */
029    public class VlaHistoricalCalibratorData
030    {
031      //============================================================================
032      // COLUMN HEADINGS
033      //============================================================================
034      
035      private static final ArrayList<String> NAME_LINE_COLUMN_NAMES =
036        new ArrayList<String>();
037      
038      private static final ArrayList<String> BAND_LINE_COLUMN_NAMES =
039        new ArrayList<String>();
040      
041      static
042      {
043        NAME_LINE_COLUMN_NAMES.add("IAU NAME");
044        NAME_LINE_COLUMN_NAMES.add("EQUINOX");
045        NAME_LINE_COLUMN_NAMES.add("PC");
046        NAME_LINE_COLUMN_NAMES.add("RA(hh,mm,ss)");
047        NAME_LINE_COLUMN_NAMES.add("DEC(ddd,mm,ss)");
048        NAME_LINE_COLUMN_NAMES.add("POS.REF");
049        NAME_LINE_COLUMN_NAMES.add("ALT.NAME");
050        
051        BAND_LINE_COLUMN_NAMES.add("BAND WAVELENGTH");
052        BAND_LINE_COLUMN_NAMES.add("BAND CODE");
053        BAND_LINE_COLUMN_NAMES.add("A");
054        BAND_LINE_COLUMN_NAMES.add("B");
055        BAND_LINE_COLUMN_NAMES.add("C");
056        BAND_LINE_COLUMN_NAMES.add("D");
057        BAND_LINE_COLUMN_NAMES.add("FLUX(Jy)");
058        BAND_LINE_COLUMN_NAMES.add("UVMIN(kL)");
059        BAND_LINE_COLUMN_NAMES.add("UVMAX(kL)");
060        BAND_LINE_COLUMN_NAMES.add("IMAGE1");
061        BAND_LINE_COLUMN_NAMES.add("IMAGE2");
062      }
063      
064      /**
065       * Returns a list where each entry is the name of a column in a
066       * "name" line.
067       * @return a list of name-line column names.
068       */
069      @SuppressWarnings("unchecked")
070      public static List<String> getNameLineColumnNames()
071      {
072        return (List<String>)NAME_LINE_COLUMN_NAMES.clone();
073      }
074      
075      /**
076       * Returns a list where each entry is the name of a column in a
077       * "band" line.
078       * @return a list of band-line column names.
079       */
080      @SuppressWarnings("unchecked")
081      public static List<String> getBandLineColumnNames()
082      {
083        return (List<String>)BAND_LINE_COLUMN_NAMES.clone();
084      }
085      
086      //============================================================================
087      // 
088      //============================================================================
089    
090      private List<List<String>> nameLines;
091      private List<List<String>> bandLines;
092      
093      private VlaHistoricalCalibratorData()
094      {
095        nameLines = new ArrayList<List<String>>();
096        bandLines = new ArrayList<List<String>>();
097      }
098      
099      /**
100       * Returns a list "name" lines for this record.  There is usually one name
101       * line for J2000 and one for B1950.  Each line is a list of strings.
102       * The meaning of the n<sup>th</sup> string can be found by fetching
103       * the n<sup>th</sup> element of {@link #getNameLineColumnNames()}.
104       * 
105       * @return a list of name lines.
106       */
107      public List<List<String>> getNameLines()
108      {
109        ArrayList<List<String>> lines = new ArrayList<List<String>>();
110        
111        for (int i=0; i < nameLines.size(); i++)
112          lines.add(new ArrayList<String>(nameLines.get(i)));
113        
114        return lines;
115      }
116      
117      /**
118       * Returns a list "band" lines for this record.
119       * Each line is a list of strings.
120       * The meaning of the n<sup>th</sup> string can be found by fetching
121       * the n<sup>th</sup> element of {@link #getBandLineColumnNames()}.
122       * 
123       * @return a list of band lines.
124       */
125      public List<List<String>> getBandLines()
126      {
127        ArrayList<List<String>> lines = new ArrayList<List<String>>();
128        
129        for (int i=0; i < bandLines.size(); i++)
130          lines.add(new ArrayList<String>(bandLines.get(i)));
131        
132        return lines;
133      }
134      
135      /**
136       * Returns an HTML table that contains a row for each name line.
137       * @param tableStyle text that is added to the <tt>&lt;table&gt;</tt> tag.
138       * @return an HTML table that contains a row for each name line.
139       */
140      public String toHtmlNameTable(String tableStyle)
141      {
142        final String EOL = System.getProperty("line.separator");
143        int colCount = NAME_LINE_COLUMN_NAMES.size();
144        
145        StringBuilder buff = new StringBuilder();
146        
147        buff.append("<table");
148        if (tableStyle == null)
149          buff.append(">");
150        else
151          buff.append(" ").append(tableStyle).append(">");
152        buff.append(EOL);
153        
154        //Column headings
155        buff.append("  <tr>");
156        for (int h=0; h < colCount; h++)
157          buff.append("<th>").append(NAME_LINE_COLUMN_NAMES.get(h)).append("</th>");
158        buff.append("</tr>").append(EOL);
159        
160        //Data
161        for (List<String> nameLine : nameLines)
162        {
163          buff.append("  <tr>");
164          for (String data : nameLine)
165            buff.append("<td>").append(data).append("</td>");
166          buff.append("</tr>").append(EOL);
167        }
168        
169        buff.append("</table>").append(EOL);
170        
171        return buff.toString();
172      }
173      
174      /**
175       * Returns an HTML table that contains a row for each band line.
176       * @param tableStyle text that is added to the <tt>&lt;table&gt;</tt> tag.
177       * @return an HTML table that contains a row for each band line.
178       */
179      public String toHtmlBandTable(String tableStyle)
180      {
181        final String EOL = System.getProperty("line.separator");
182        int colCount = BAND_LINE_COLUMN_NAMES.size();
183        
184        StringBuilder buff = new StringBuilder();
185        
186        buff.append("<table");
187        if (tableStyle == null)
188          buff.append(">");
189        else
190          buff.append(" ").append(tableStyle).append(">");
191        buff.append(EOL);
192        
193        //Column headings
194        buff.append("  <tr>");
195        for (int h=0; h < colCount; h++)
196          buff.append("<th>").append(BAND_LINE_COLUMN_NAMES.get(h)).append("</th>");
197        buff.append("</tr>").append(EOL);
198    
199        final int imageCol = 9;
200        
201        //Data
202        for (List<String> bandLine : bandLines)
203        {
204          buff.append("  <tr>");
205          int col = 0;
206          for (String data : bandLine)
207          {
208            buff.append("<td>");
209            if ((col < imageCol) || data.equals("-"))
210            {
211              buff.append(data);
212            }
213            else
214            {
215              buff.append("<a href=\"").append(data).append("\">");
216              buff.append(data.substring(data.lastIndexOf('/')+1));
217              buff.append("</a>");
218            }
219            buff.append("</td>");
220            col++;
221          }
222          buff.append("</tr>").append(EOL);
223        }
224        
225        buff.append("</table>").append(EOL);
226    
227        return buff.toString();
228      }
229      
230      /**
231       * Creates a new historical record by parsing the given text.
232       * The text is expected to be in the same format as that which is
233       * found in the "historical record" field of the new VLA Calibrator
234       * Database.
235       * 
236       * @param text to be parsed.
237       * @return a new record.
238       * 
239       * @throws IllegalArgumentException if {@code text} is not in the
240       *           expected format.
241       */
242      public static VlaHistoricalCalibratorData parse(String text)
243      {
244        VlaHistoricalCalibratorData data = new VlaHistoricalCalibratorData();
245        
246        if (isOldVlaRecord(text))
247        {
248          Map<String, String> map = data.makeMap(text);
249          
250          data.makeNameLine(map, "J2000");
251          data.makeNameLine(map, "B1950");
252          data.makeBandLines(map);
253        }
254        
255        return data;
256      }
257      
258      /**
259       * Returns <i>true</i> if it looks like {@code text} is in a form
260       * that the {@link #parse(String)} method of this class understands.
261       * Specifically, this method will return <i>true</i> if {@code text}
262       * contains the string "origin=VLA.Calibrator.Catalog".
263       * 
264       * @param text a potential VLA Calibrator Manual entry.
265       * 
266       * @return <i>true</i> if the given text looks like it could be an
267       *         old VLA Calibrator Manual entry.
268       */
269      public static boolean isOldVlaRecord(String text)
270      {
271        return text.contains("origin=VLA.Calibrator.Catalog");
272      }
273    
274      /**
275       * @param map
276       */
277      private void makeNameLine(Map<String, String> map, String prefix)
278      {
279        ArrayList<String> line = new ArrayList<String>();
280    
281        final String[] KEYS = {".IAU.NAME", ".POS.CODE", ".RA", ".DEC",
282                               ".POS.REF", ".ALT.NAME"};
283        
284        for (String key : KEYS)
285        {
286          String value = map.get(prefix + key);
287          line.add(value == null ? " " : value);
288        }
289        
290        line.add(1, prefix);
291        
292        nameLines.add(line);
293      }
294    
295      private void makeBandLines(Map<String,String> map)
296      {
297        //Find out what bands we have
298        ArrayList<String> bandCodes = new ArrayList<String>();
299        
300        for (String key : map.keySet())
301          if (key.startsWith("BAND.") && key.endsWith(".WAVELENGTH"))
302          {
303            //Form of key s/b "BAND.?.WAVELENGTH"
304            String[] s = key.split("\\.");
305            bandCodes.add(s[1]);
306          }
307        
308        //One line per band code
309        for (String bandCode : bandCodes)
310          makeBandLine(map, "BAND."+bandCode);
311        
312        //Sort from largest wavelength to smallest
313        Collections.sort(bandLines, new Comparator<List<String>>() {
314          public int compare(List<String> s1, List<String> s2)
315          {
316            String w1 = s1.get(0).replace("cm", "");
317            String w2 = s2.get(0).replace("cm", "");
318            return Double.valueOf(w2).compareTo(Double.valueOf(w1));
319          }
320        });
321      }
322      
323      private void makeBandLine(Map<String, String> map, String prefix)
324      {
325        ArrayList<String> line = new ArrayList<String>();
326    
327        final String[] KEYS = {".WAVELENGTH", ".QUALITY.A", ".QUALITY.B",
328                               ".QUALITY.C", ".QUALITY.D", ".FLUX",
329                               ".UV.MIN", ".UV.MAX", ".PLOT.1", ".PLOT.2"};
330        
331        for (String key : KEYS)
332        {
333          String value = map.get(prefix + key);
334          line.add(value == null ? "-" : value);
335        }
336        
337        //Prefix is "BAND.?"; we want the value of "?" -- the band code
338        line.add(1, prefix.substring(5, 6));
339        
340        bandLines.add(line);
341      }
342      
343      /**
344       * @param text
345       * @return
346       */
347      private Map<String, String> makeMap(String text)
348      {
349        HashMap<String, String> map = new HashMap<String, String>();
350        
351        String[] entries = text.split(";");
352        
353        if (entries.length < 1)
354          throw new IllegalArgumentException(
355            "Found no name/value pairs in parameter '" + text + "'.");
356        
357        for (String entry : entries)
358        {
359          String[] keyValue = entry.split("=");
360          
361          if (keyValue.length != 2)
362          {
363            throw new IllegalArgumentException(
364              "Expected format: name=value.  Found '" + entry +
365              "' in paramter '" + text + "'.");
366          }
367          
368          map.put(keyValue[0], keyValue[1]);
369        }
370    
371        return map;
372      }
373      /*
374      public static void main(String[] args)
375      {
376        String text = "origin=VLA.Calibrator.Catalog;J2000.IAU.NAME=2344+824;J2000.POS.CODE=C;J2000.RA=23h44m03.7718s;J2000.DEC=82d26'40.401\";J2000.POS.REF=Jan97;J2000.ALT.NAME=;B1950.IAU.NAME=2342+821;B1950.POS.CODE=C;B1950.RA=23h42m06.4253s;B1950.DEC=82d10'01.320\";B1950.POS.REF=;B1950.ALT.NAME=;BAND.L.WAVELENGTH=20.0cm;BAND.L.QUALITY.A=P;BAND.L.QUALITY.B=P;BAND.L.QUALITY.C=P;BAND.L.QUALITY.D=P;BAND.L.FLUX=3.79Jy;BAND.L.UV.MIN=;BAND.L.UV.MAX=;BAND.L.PLOT.1=http://www.vla.nrao.edu/astro/calib/manual/calplots/20cmC/2344+824.1.ps.Z;BAND.L.PLOT.2=http://www.vla.nrao.edu/astro/calib/manual/calplots/20cmC/2344+824.uv.ps.Z;BAND.C.WAVELENGTH=6.0cm;BAND.C.QUALITY.A=S;BAND.C.QUALITY.B=P;BAND.C.QUALITY.C=P;BAND.C.QUALITY.D=P;BAND.C.FLUX=1.34Jy;BAND.C.UV.MIN=;BAND.C.UV.MAX=;BAND.C.PLOT.1=http://www.vla.nrao.edu/astro/calib/manual/calplots/6cmC/2344+824.1.ps.Z;BAND.C.PLOT.2=http://www.vla.nrao.edu/astro/calib/manual/calplots/6cmC/2344+824.uv.ps.Z;BAND.X.WAVELENGTH=3.7cm;BAND.X.QUALITY.A=S;BAND.X.QUALITY.B=S;BAND.X.QUALITY.C=P;BAND.X.QUALITY.D=P;BAND.X.FLUX=0.75Jy;BAND.X.UV.MIN=;BAND.X.UV.MAX=200kL;BAND.X.PLOT.1=http://www.vla.nrao.edu/astro/calib/manual/calplots/3.7cmC/2344+824.1.ps.Z;BAND.X.PLOT.2=http://www.vla.nrao.edu/astro/calib/manual/calplots/3.7cmC/2344+824.uv.ps.Z;BAND.U.WAVELENGTH=2.0cm;BAND.U.QUALITY.A=W;BAND.U.QUALITY.B=W;BAND.U.QUALITY.C=W;BAND.U.QUALITY.D=W;BAND.U.FLUX=0.4Jy;BAND.U.UV.MIN=;BAND.U.UV.MAX=;BAND.Q.WAVELENGTH=0.7cm;BAND.Q.QUALITY.A=W;BAND.Q.QUALITY.B=W;BAND.Q.QUALITY.C=W;BAND.Q.QUALITY.D=W;BAND.Q.FLUX=0.3Jy;BAND.Q.UV.MIN=;BAND.Q.UV.MAX=; 0";
377        VlaHistoricalCalibratorData data = VlaHistoricalCalibratorData.parse(text);
378        
379        for (List<String> line : data.getNameLines())
380          System.out.println(line);
381        
382        for (List<String> line : data.getBandLines())
383          System.out.println(line);
384        
385        System.out.println();
386        System.out.println(data.toHtmlNameTable("border=\"1\""));
387        System.out.println(data.toHtmlBandTable("border=\"1\""));
388      }
389      */
390    }