001 package edu.nrao.sss.model.source.parser; 002 003 import java.io.IOException; 004 import java.io.LineNumberReader; 005 import java.io.Reader; 006 import java.net.URL; 007 import java.util.ArrayList; 008 import java.util.HashMap; 009 import java.util.List; 010 import java.util.Map; 011 012 import static edu.nrao.sss.util.StringUtil.EOL; 013 014 import edu.nrao.sss.model.source.Source; 015 import edu.nrao.sss.model.source.SourceCatalog; 016 017 /** 018 * A parser of the 019 * <a href="http://www.vla.nrao.edu/astro/calib/manual/csource.html"> 020 * VLA Calibrator Manual</a> HTML page. 021 * This parser turns the data on the above page into a 022 * {@link edu.nrao.sss.model.source.SourceCatalog}. 023 * <p> 024 * <b>Version Info:</b> 025 * <table style="margin-left:2em"> 026 * <tr><td>$Revision: 1324 $</td></tr> 027 * <tr><td>$Date: 2008-06-04 16:42:24 -0600 (Wed, 04 Jun 2008) $</td></tr> 028 * <tr><td>$Author: dharland $</td></tr> 029 * </table></p> 030 * 031 * @author David M. Harland 032 * @since 2006-12-05 033 */ 034 public class VlaCalibDbHtmlReader 035 extends AbstractSourceCatalogReader 036 { 037 private URL baseUrlForLinks; 038 private Source source; 039 040 private VlaCalibNameLineReader nameReader = new VlaCalibNameLineReader(); 041 private VlaCalibBandLineReader bandReader = new VlaCalibBandLineReader(); 042 043 private LineNumberReader dataReader; 044 045 /** Creates a new instance. */ 046 public VlaCalibDbHtmlReader() 047 { 048 this(null); 049 } 050 051 /** 052 * Creates a new instance. 053 * @param parentUrlForLinks serves as the parent URL for any relative URLs 054 * found by this reader. 055 */ 056 public VlaCalibDbHtmlReader(URL parentUrlForLinks) 057 { 058 baseUrlForLinks = parentUrlForLinks; 059 060 nameReader = new VlaCalibNameLineReader(); 061 bandReader = new VlaCalibBandLineReader(); 062 063 bandReader.baseUrlForLinks = baseUrlForLinks; 064 } 065 066 /** Prepares this instance to read from a new source. */ 067 private void reset(Reader in, SourceCatalog destination) 068 { 069 //leave baseUrlForLinks as is 070 catalog = (destination == null) ? new SourceCatalog() : destination; 071 readWasSuccessful = false; 072 errors.clear(); 073 dataReader = new LineNumberReader(in); 074 } 075 076 /** 077 * Sets the base URL for relative links found by this reader. 078 * @param parentUrlForLinks serves as the parent URL for any relative URLs 079 * found by this reader. 080 */ 081 public void setBaseUrlForLinks(URL parentUrlForLinks) 082 { 083 baseUrlForLinks = parentUrlForLinks; 084 bandReader.baseUrlForLinks = baseUrlForLinks; 085 } 086 087 /* (non-Javadoc) 088 * @see SourceCatalogReader#setPrefixForHistoricalRecords(String) 089 */ 090 @Override 091 public void setPrefixForHistoricalRecords(String prefix) 092 { 093 super.setPrefixForHistoricalRecords(prefix); 094 095 nameReader.histRecPrefix = "origin=" + histRecPrefix + ";"; 096 bandReader.histRecPrefix = "origin=" + histRecPrefix + ";"; 097 } 098 099 /** 100 * NOTE: This method closes the incoming Reader! 101 * 102 * @see AbstractSourceCatalogReader#read(java.io.Reader, 103 * edu.nrao.sss.model.source.SourceCatalog) 104 */ 105 public boolean read(Reader in, SourceCatalog destination) 106 { 107 reset(in, destination); 108 109 String line = getFirstDataLine(); 110 111 while (line != null) // && getErrorCount() < 10) 112 { 113 source = new Source(); 114 115 if (sourceInfoOrigin != null) 116 source.setOriginOfInformation(sourceInfoOrigin); 117 118 catalog.addItem(source); 119 120 //J2000 line 121 nameReader.parse(line, dataReader.getLineNumber()).fillSource(source); 122 errors.addAll(nameReader.errors); 123 124 String posRefJ2000 = nameReader.posRef; 125 126 line = getNextDataLine(); 127 128 //B1950 line 129 nameReader.parse(line, dataReader.getLineNumber()).fillSource(source); 130 errors.addAll(nameReader.errors); 131 132 line = getNextDataLine(); 133 134 //Should be band lines. Might not have any 135 if (dataLooksLikeNameLine(line)) 136 { 137 putError(nameReader.iauName + ": Found no band data for source."); 138 } 139 else //we have at least one band line or something really bogus 140 { 141 while (lineLooksLikeData(line)) 142 { 143 bandReader.parse(line, dataReader.getLineNumber(), nameReader.iauName); 144 bandReader.fillSource(source, posRefJ2000); 145 errors.addAll(bandReader.errors); 146 line = getNextLine(); 147 } 148 149 makeCalQualTable(source); 150 151 //If not at end of file, assume next data line is J2000 line 152 if (line != null) 153 line = getNextDataLine(); 154 } 155 } 156 157 source = null; 158 159 try 160 { 161 this.dataReader.close(); 162 } 163 164 catch(IOException ioe) 165 { 166 putError(0, "Error closing Reader: " + ioe.getMessage()); 167 } 168 169 readWasSuccessful = getErrorCount() == 0; 170 171 return readWasSuccessful; 172 } 173 174 private Map<String, String> nameValue = new HashMap<String, String>(); 175 private List<String> bandCodes = new ArrayList<String>(); 176 177 private static final String DELIM = " "; 178 179 private void makeCalQualTable(Source src) 180 { 181 if (src.getHistoricalRecords().size() == 0) 182 return; 183 184 nameValue.clear(); 185 bandCodes.clear(); 186 187 //Parse the historical record into a map 188 String histRec = src.getHistoricalRecords().get(0); 189 for (String nv : histRec.split(";")) 190 { 191 String[] s = nv.split("="); 192 if (s[0].startsWith("BAND")) 193 { 194 nameValue.put(s[0], s.length == 1 ? "" : s[1]); 195 if (s[0].endsWith("WAVELENGTH")) 196 bandCodes.add(s[0].substring(5, 6)); //BAND.x.BLAH 197 } 198 } 199 200 if (nameValue.size() == 0) 201 return; 202 203 //Build the note 204 StringBuilder note = new StringBuilder(" BAND A B C D"); 205 note.append(EOL); 206 note.append("======== = = = =").append(EOL); 207 208 for (String bandCode : bandCodes) 209 { 210 String prefix = "BAND."+bandCode+"."; 211 note.append(bandCode).append(DELIM); 212 String wavelen = nameValue.get(prefix+"WAVELENGTH"); 213 int pad = Math.max(0, 6-wavelen.length()); 214 for (int p=1; p <= pad; p++) 215 note.append(' '); 216 note.append(wavelen).append(DELIM); 217 note.append(nameValue.get(prefix+"QUALITY.A")).append(DELIM); 218 note.append(nameValue.get(prefix+"QUALITY.B")).append(DELIM); 219 note.append(nameValue.get(prefix+"QUALITY.C")).append(DELIM); 220 note.append(nameValue.get(prefix+"QUALITY.D")).append(EOL); 221 } 222 223 src.getNotes().add(note.toString()); 224 } 225 226 //============================================================================ 227 // LINE READING 228 //============================================================================ 229 230 /** Returns the first data line in the reader, or null if EOF. */ 231 private String getFirstDataLine() 232 { 233 String line = getNextLine(); 234 235 //First line of table starts with "IAU NAME" 236 while (!line.startsWith("IAU")) 237 line = getNextLine(); 238 239 return getNextDataLine(); 240 } 241 242 /** Returns the next data line, or null if EOF. */ 243 private String getNextDataLine() 244 { 245 String line = getNextLine(); 246 247 while (line != null && !lineLooksLikeData(line)) 248 line = getNextLine(); 249 250 return line; 251 } 252 253 /** Reads lines, returning first one that does not cause an error. */ 254 private String getNextLine() 255 { 256 int attempt = 0; 257 258 while (++attempt <= 100) 259 { 260 try { 261 String line = dataReader.readLine(); 262 return (line.contains("</PRE>") || line.contains("</pre>")) ? null 263 : line; 264 } 265 catch (IOException ex) { 266 putError("I/O Exception: " + ex.getMessage()); 267 } 268 } 269 270 throw new RuntimeException("Failed " + attempt + 271 " times while attempting to read a line."); 272 } 273 274 /** Decides whether or not line contains data. */ 275 private boolean lineLooksLikeData(String line) 276 { 277 return line != null && //End of file 278 line.length() > 0 && //Empty line 279 !line.startsWith("====") && //Separator 280 !line.startsWith("----") && //Separator 281 !line.startsWith("BAND") && //Separator 282 line.replaceAll(" ", "").length() > 0; //Blank line 283 } 284 285 /** Returns true if it looks like line is a name line. */ 286 private boolean dataLooksLikeNameLine(String line) 287 { 288 if (line == null) 289 return false; 290 291 char signChar = line.charAt(4); 292 293 return signChar == '+' || signChar == '-'; 294 } 295 296 /** Adds a new parsing error to our list. */ 297 private void putError(String message) 298 { 299 putError(dataReader.getLineNumber(), message); 300 } 301 }