Issue #2905 added translation tables for BUFR

added current WMO BUFR code/flag XML table file
created parser that can read the WMO XML
created JAXB files for BUFR translation XML files
created utility to generate translation tables for specific BUFR tables


Former-commit-id: 2634afede20af4ab6d059ef6dbd1af093184f90a
This commit is contained in:
Brian Clements 2014-03-26 08:20:04 -05:00
parent fdbb505679
commit d9eb75280d
9 changed files with 38500 additions and 4 deletions

View file

@ -2,10 +2,14 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: NetCDF Bufr
Bundle-SymbolicName: com.raytheon.uf.common.nc.bufr
Bundle-Version: 14.3
Bundle-Version: 1.14.0
Bundle-Vendor: RAYTHEON
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: ucar.nc2,
ucar.nc2.bufrsplitter,
com.raytheon.uf.common.status
Export-Package: com.raytheon.uf.common.nc.bufr
Export-Package: com.raytheon.uf.common.nc.bufr,
com.raytheon.uf.common.nc.bufr.tables,
com.raytheon.uf.common.nc.bufr.util
Bundle-ClassPath: .,
res/

View file

@ -1,4 +1,8 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
bin.includes = .,\
res/,\
bin/,\
META-INF/
src.includes = res/,\
bin/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
WMO BUFR code tables obtained from http://www.wmo.int/pages/prog/www/WMOCodes/WMO306_vI2/LatestVERSION/LatestVERSION.html

View file

@ -0,0 +1,97 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.nc.bufr.tables;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
/**
* Foreign parameter value mapped to translation table entry. Allows for
* parameter values to be associated with BUFR table values.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 24, 2014 2905 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
@XmlAccessorType(XmlAccessType.NONE)
public class MappedValue {
@XmlAttribute(required = true)
private String parameter;
@XmlAttribute(required = false)
private String value;
/**
*
*/
public MappedValue() {
}
/**
* @param parameter
* @param value
*/
public MappedValue(String parameter, String value) {
this.parameter = parameter;
this.value = value;
}
/**
* @return the parameter
*/
public String getParameter() {
return parameter;
}
/**
* @param parameter
* the parameter to set
*/
public void setParameter(String parameter) {
this.parameter = parameter;
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value
* the value to set
*/
public void setValue(String value) {
this.value = value;
}
}

View file

@ -0,0 +1,150 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.nc.bufr.tables;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Entry in BUFR translation table.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 24, 2014 2905 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class TableEntry {
@XmlAttribute(required = true)
private String indexRange;
@XmlAttribute(required = false)
private String description;
@XmlElement(name = "mappedValue")
private List<MappedValue> mappedValues;
/**
*
*/
public TableEntry() {
}
/**
* @param indexRange
* list of BUFR table indexes in the format
* '[0-9]+|[0-9]+-[0-9]+'
* @param mappedValues
* storage of foreign values mapped to this index range
*/
public TableEntry(String indexRange, List<MappedValue> mappedValues) {
this.indexRange = indexRange;
this.mappedValues = mappedValues;
}
/**
* @param indexRange
* list of BUFR table indexes in the format
* '[0-9]+|[0-9]+-[0-9]+'
* @param description
* BUFR table text for this index range
* @param mappedValues
* storage of foreign values mapped to this index range
*/
public TableEntry(String indexRange, String description,
List<MappedValue> mappedValues) {
this(indexRange, mappedValues);
this.description = description;
}
/**
* Append a foreign value to be mapped to this index range
*
* @param value
*/
public void addMappedValue(MappedValue value) {
if (this.mappedValues == null) {
this.mappedValues = new ArrayList<MappedValue>();
}
this.mappedValues.add(value);
}
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @param description
* the description to set
*/
public void setDescription(String description) {
this.description = description;
}
/**
* @return the mappedValues
*/
public List<MappedValue> getMappedValues() {
return mappedValues;
}
/**
* @param mappedValues
* the mappedValues to set
*/
public void setMappedValues(List<MappedValue> mappedValues) {
this.mappedValues = mappedValues;
}
/**
* @return the indexRange
*/
public String getIndexRange() {
return indexRange;
}
/**
* @param indexRange
* the indexRange to set
*/
public void setIndexRange(String indexRange) {
this.indexRange = indexRange;
}
}

View file

@ -0,0 +1,133 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.nc.bufr.tables;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Represents a BUFR table that also has foreign values mapped to table entries.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 24, 2014 2905 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class TranslationTable {
@XmlAttribute(required = true)
private String bufrTable;
@XmlAttribute(required = false)
private String description;
@XmlElement(name = "entry")
private List<TableEntry> entries;
/**
*
*/
public TranslationTable() {
}
/**
* @param bufrTable
* BUFR table identifier (FXY) in the format 'F XX YYY'
* @param entries
* storage for table entries
*/
public TranslationTable(String bufrTable, List<TableEntry> entries) {
this.bufrTable = bufrTable;
this.entries = entries;
}
/**
* @param bufrTable
* BUFR table identifier (FXY) in the format 'F XX YYY'
* @param description
* human readable name of table
* @param entries
* storage for table entries
*/
public TranslationTable(String bufrTable, String description,
List<TableEntry> entries) {
this(bufrTable, entries);
this.description = description;
}
/**
* Append new entry to entries
*
* @param entry
*/
public void addEntry(TableEntry entry) {
if (this.entries == null) {
this.entries = new ArrayList<TableEntry>();
}
this.entries.add(entry);
}
/**
* @return the bufrTable
*/
public String getBufrTable() {
return bufrTable;
}
/**
* @param bufrTable
* the bufrTable to set
*/
public void setBufrTable(String bufrTable) {
this.bufrTable = bufrTable;
}
/**
* @return the entries
*/
public List<TableEntry> getEntries() {
return entries;
}
/**
* @param entries
* the entries to set
*/
public void setEntries(List<TableEntry> entries) {
this.entries = entries;
}
}

View file

@ -0,0 +1,203 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.nc.bufr.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Set;
import java.util.regex.Matcher;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.stream.XMLStreamException;
import com.raytheon.uf.common.nc.bufr.tables.TranslationTable;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
/**
* Utility to generate initial translation table XML files from WMO BUFR code
* table XML document.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 24, 2014 2905 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class TranslationTableGenerator {
private static final IUFStatusHandler log = UFStatus
.getHandler(TranslationTableGenerator.class);
public static final String XML_TABLE_RESOURCE = "res/tables/BUFRCREX_21_0_0_CodeFlag_en.xml";
/**
* Load default WMO BUFR tables XML file into inputstream
*
* @return
* @throws IOException
*/
public static InputStream getBufrTables() throws IOException {
ClassLoader loader = TranslationTableGenerator.class.getClassLoader();
InputStream rval = loader.getResourceAsStream(XML_TABLE_RESOURCE);
if (rval == null) {
File f = new File(XML_TABLE_RESOURCE);
if (f.exists()) {
rval = new FileInputStream(f);
} else {
log.error("Unable to find bufr tables: " + XML_TABLE_RESOURCE);
throw new FileNotFoundException("Unable to find bufr tables: "
+ XML_TABLE_RESOURCE);
}
}
return rval;
}
/**
* Generate base translation table XML files
*
* @param destDir
* destination directory for XML files
* @param includedTables
* list of included table ids in the format 'F XX YYY'
* @param createPlaceholders
* if true, placeholders for mapped values will be created in
* each table entry
* @throws XMLStreamException
* @throws JAXBException
* @throws IOException
*/
public static void generate(File destDir, Set<String> includedTables,
boolean createPlaceholders) throws XMLStreamException,
JAXBException, IOException {
InputStream bufrTables = getBufrTables();
try {
generate("", destDir, includedTables, createPlaceholders,
bufrTables);
} finally {
bufrTables.close();
}
}
/**
* Generate base translation table XML files
*
* @param tableFilePrefix
* string that will prefix generated files
* @param destDir
* destination directory for XML files
* @param includedTables
* list of included table ids in the format 'F XX YYY'
* @param createPlaceholders
* if true, placeholders for mapped values will be created in
* each table entry
* @param bufrTables
* stream to WMO BUFR XML code tables
* @throws XMLStreamException
* @throws JAXBException
*/
public static void generate(String tableFilePrefix, File destDir,
Set<String> includedTables, boolean createPlaceholders,
InputStream bufrTables) throws XMLStreamException, JAXBException {
Collection<TranslationTable> tables = createTables(includedTables,
createPlaceholders, bufrTables);
Marshaller marsh = getMarshaller();
for (TranslationTable table : tables) {
StringBuilder sb = new StringBuilder();
if (tableFilePrefix != null && !tableFilePrefix.isEmpty()) {
sb.append(tableFilePrefix).append('-');
}
sb.append(replaceWhiteSpace(table.getBufrTable(), "_"));
sb.append(".xml");
File tableFile = new File(destDir, sb.toString());
log.info("Writing translation table to "
+ tableFile.getAbsolutePath());
marsh.marshal(table, tableFile);
}
}
/**
* Replace all white space characters in str with replacement
*
* @param str
* @param replacement
* @return
*/
private static String replaceWhiteSpace(String str, String replacement) {
Matcher m = WmoCodeTableParser.WHITESPACE.matcher(str);
return m.replaceAll(replacement);
}
/**
* Get JAXB marshaller (caches result)
*
* @return
* @throws JAXBException
*/
private static Marshaller getMarshaller() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(TranslationTable.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
return marshaller;
}
/**
* Read WMO BUFR code tables file and create TranslationTable JAXB objects
*
* @param includedTables
* list of included table ids in the format 'F XX YYY'
* @param createPlaceholders
* if true, placeholders for mapped values will be created in
* each table entry
* @param bufrTables
* stream to WMO BUFR XML code tables
* @return
* @throws XMLStreamException
*/
public static Collection<TranslationTable> createTables(
Set<String> includedTables, boolean createPlaceholders,
InputStream bufrTables) throws XMLStreamException {
Collection<TranslationTable> rval;
WmoCodeTableParser parser = new WmoCodeTableParser(bufrTables);
try {
log.info("Starting to process bufr tables");
rval = parser.parseTables(includedTables, createPlaceholders);
log.info("Done processing bufr tables");
} finally {
parser.close();
}
return rval;
}
}

View file

@ -0,0 +1,298 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.nc.bufr.util;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import com.raytheon.uf.common.nc.bufr.tables.MappedValue;
import com.raytheon.uf.common.nc.bufr.tables.TableEntry;
import com.raytheon.uf.common.nc.bufr.tables.TranslationTable;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
/**
* Parsing utility for WMO BUFR code table XML
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 24, 2014 2905 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class WmoCodeTableParser {
private static final IUFStatusHandler log = UFStatus
.getHandler(WmoCodeTableParser.class);
public static final String FXY_ELEMENT = "FXY";
public static final String TABLE_DESC_ELEMENT = "ElementName_en";
public static final String ENTRY_ELEMENT_PREFIX = "BUFRCREX";
public static final String INDEX_RANGE_ELEMENT = "CodeFigure";
public static final String VALUE_ELEMENT = "EntryName_en";
private static final XMLInputFactory staxFactory = XMLInputFactory
.newInstance();
private final XMLStreamReader reader;
private static final Pattern FXY_PATTERN = Pattern
.compile("(\\d)(\\d\\d)(\\d\\d\\d)");
protected static final Pattern WHITESPACE = Pattern.compile("\\s");
/**
* @param bufrTables
* stream to WMO BUFR XML code table file
* @throws XMLStreamException
*/
public WmoCodeTableParser(InputStream bufrTables) throws XMLStreamException {
this.reader = staxFactory.createXMLStreamReader(bufrTables);
}
/**
* Closes open resources, must be called after parsing
*
* @throws XMLStreamException
*/
public void close() throws XMLStreamException {
this.reader.close();
}
/**
* Create TranslationTable objects for each table in WMO XML file
*
* @param createPlaceholders
* if true, placeholders for mapped values will be created in
* each table entry
* @return
* @throws XMLStreamException
*/
public Collection<TranslationTable> parseAllTables(
boolean createPlaceholders) throws XMLStreamException {
return parseTables(Collections.<String> emptySet(), createPlaceholders);
}
/**
* Create a TranslationTable object for each id in includedTables
*
* @param includedTables
* list of included table ids in the format 'F XX YYY'
* @param createPlaceholders
* if true, placeholders for mapped values will be created in
* each table entry
* @return
* @throws XMLStreamException
*/
public Collection<TranslationTable> parseTables(Set<String> includedTables,
boolean createPlaceholders) throws XMLStreamException {
Set<String> includedFXYs = unformatSet(includedTables);
boolean parseAll = includedTables.isEmpty();
List<TranslationTable> rval = new ArrayList<TranslationTable>();
TranslationTable currentTable = null;
String fxy = null;
String desc = null;
String index = null;
String value = null;
while (reader.hasNext()) {
switch (reader.next()) {
/* read text for each element in entry */
case XMLStreamReader.START_ELEMENT:
if (isElement(FXY_ELEMENT)) {
fxy = reader.getElementText().trim();
} else if (isElement(TABLE_DESC_ELEMENT)) {
desc = reader.getElementText().trim();
} else if (isElement(INDEX_RANGE_ELEMENT)) {
index = reader.getElementText().trim();
} else if (isElement(VALUE_ELEMENT)) {
value = reader.getElementText().trim();
}
break;
case XMLStreamReader.END_ELEMENT:
/* we can only decide what to do after we see entire entry */
if (elementHasPrefix(ENTRY_ELEMENT_PREFIX)) {
if (fxy == null) {
break;
}
if (currentTable == null || !isSameTable(currentTable, fxy)) {
if (currentTable != null) {
/* starting a new table, finish previous table */
finalizeTable(rval, currentTable, includedFXYs,
parseAll);
if (includedFXYs.isEmpty()) {
/* early exit */
return rval;
}
}
currentTable = new TranslationTable(fxy, desc,
new ArrayList<TableEntry>());
}
/* some entries are just descriptive */
if (index != null && value != null) {
currentTable.addEntry(createEntry(index, value,
createPlaceholders));
}
fxy = desc = index = value = null;
}
break;
}
}
/* take care of last table being processed */
if (currentTable != null) {
finalizeTable(rval, currentTable, includedFXYs, parseAll);
}
if (!includedFXYs.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (String str : includedFXYs) {
sb.append(str).append(", ");
}
log.warn("Requested BUFR table(s) not found in file: "
+ sb.toString());
}
return rval;
}
/**
* Add current table to storage if included in parsing result
*
* @param storage
* @param currentTable
* @param includedFXYs
* @param parseAll
*/
private void finalizeTable(List<TranslationTable> storage,
TranslationTable currentTable, Set<String> includedFXYs,
boolean parseAll) {
String currentName = currentTable.getBufrTable();
if (parseAll || includedFXYs.contains(currentName)) {
includedFXYs.remove(currentName);
formatTableName(currentTable);
storage.add(currentTable);
}
}
/**
* Create a table entry
*
* @param indexRange
* @param value
* @param createPlaceholders
* if true, placeholders for mapped values will be created in
* each table entry
* @return
*/
private TableEntry createEntry(String indexRange, String value,
boolean createPlaceholders) {
TableEntry entry = new TableEntry(indexRange, value,
new ArrayList<MappedValue>());
if (createPlaceholders) {
entry.addMappedValue(new MappedValue("parameter", "value"));
}
return entry;
}
/**
* Convert from conventional 'F XX YYY' format to 'FXXYYY' format used in
* WMO XML
*
* @param in
* @return
*/
private Set<String> unformatSet(Set<String> in) {
Set<String> rval = new HashSet<String>(in.size());
for (String value : in) {
Matcher m = WHITESPACE.matcher(value);
rval.add(m.replaceAll(""));
}
return rval;
}
/**
* @param table
* @param id
* @return true if the translation table has the provided id
*/
private boolean isSameTable(TranslationTable table, String id) {
return table.getBufrTable().equals(id);
}
/**
* Convert from 'FXXYYY' format used in WMO XML to conventional 'F XX YYY'
* format
*
* @param table
*/
private void formatTableName(TranslationTable table) {
String oldName = table.getBufrTable();
Matcher m = FXY_PATTERN.matcher(oldName);
String newName;
if (m.matches()) {
newName = m.group(1) + " " + m.group(2) + " " + m.group(3);
} else {
log.warn("Table name does not match expected format: " + oldName);
newName = oldName;
}
table.setBufrTable(newName);
}
/**
* @param name
* @return true if the current element in the reader has name for the local
* name
*/
private boolean isElement(String name) {
QName currentElement = reader.getName();
return currentElement.getLocalPart().equalsIgnoreCase(name);
}
/**
* @param prefix
* @return true if the reader's current element's local name starts with
* prefix
*/
private boolean elementHasPrefix(String prefix) {
QName currentElement = reader.getName();
return currentElement.getLocalPart().startsWith(prefix);
}
}