diff --git a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrDataItem.java b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrDataItem.java
new file mode 100644
index 0000000000..f59430406f
--- /dev/null
+++ b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrDataItem.java
@@ -0,0 +1,87 @@
+/**
+ * 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;
+
+import ucar.ma2.DataType;
+import ucar.nc2.Variable;
+
+/**
+ * Parsed BUFR data field value bundled with metadata
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 31, 2014 2905 bclement Initial creation
+ *
+ *
+ *
+ * @author bclement
+ * @version 1.0
+ */
+public class BufrDataItem {
+
+ private final String name;
+
+ private final Object value;
+
+ private final DataType type;
+
+ private final Variable variable;
+
+ public BufrDataItem(String name, Object value, DataType type,
+ Variable variable) {
+ this.name = name;
+ this.value = value;
+ this.type = type;
+ this.variable = variable;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the value
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * @return the type
+ */
+ public DataType getType() {
+ return type;
+ }
+
+ /**
+ * @return the variable
+ */
+ public Variable getVariable() {
+ return variable;
+ }
+
+}
diff --git a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrFileSeparator.java b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrFileSeparator.java
new file mode 100644
index 0000000000..d08421f5b4
--- /dev/null
+++ b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrFileSeparator.java
@@ -0,0 +1,132 @@
+/**
+ * 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;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import ucar.nc2.iosp.bufr.writer.BufrSplitter;
+import ucar.nc2.iosp.bufr.writer.BufrSplitter.Options;
+
+import com.raytheon.uf.common.status.IUFStatusHandler;
+import com.raytheon.uf.common.status.UFStatus;
+
+/**
+ * Utility to split mixed-type BUFR files into separate messages. Creates a new
+ * BUFR file on the file system for each separate message.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Apr 1, 2014 2905 bclement Initial creation
+ *
+ *
+ *
+ * @author bclement
+ * @version 1.0
+ */
+public class BufrFileSeparator {
+
+ private static final IUFStatusHandler log = UFStatus
+ .getHandler(BufrFileSeparator.class);
+
+ public static final File DEFAULT_TMP_DIR;
+
+ static {
+ final String edexHomeProp = "edex.home";
+ String baseDir = System.getProperty(edexHomeProp);
+ if (baseDir == null || baseDir.trim().isEmpty()) {
+ log.warn("Property '" + edexHomeProp
+ + "' not set, defaulting to system tmp directory");
+ DEFAULT_TMP_DIR = new File(System.getProperty("java.io.tmpdir"));
+ } else {
+ DEFAULT_TMP_DIR = new File(baseDir + File.separator + "data",
+ "processing");
+ }
+ }
+
+ private static final FilenameFilter BUFR_FILTER = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".bufr");
+ }
+ };
+
+ /**
+ * Splits the mixed BUFR file into homogeneous BUFR files that are written
+ * to the file system.
+ *
+ * @param mixedBufrFile
+ * @return list of absolute paths to new BUFR files
+ * @throws IOException
+ */
+ public static List separate(File mixedBufrFile) throws IOException {
+ final File outputBaseDir = DEFAULT_TMP_DIR;
+ final String inputFile = mixedBufrFile.getAbsolutePath();
+ final File outputDir = getOutputDir(mixedBufrFile.getName(),
+ outputBaseDir);
+ Options options = new Options() {
+ @Override
+ public String getFileSpec() {
+ return inputFile;
+ }
+
+ @Override
+ public String getDirOut() {
+ return outputDir.getAbsolutePath();
+ }
+ };
+
+ BufrSplitter splitter = new BufrSplitter(options);
+ splitter.execute();
+
+ File[] files = outputDir.listFiles(BUFR_FILTER);
+ List rval = new ArrayList(files.length);
+ for (File f : files) {
+ rval.add(f.getAbsolutePath());
+ }
+ return rval;
+ }
+
+ /**
+ * Create a temporary output directory based on the input file name
+ *
+ * @param inputName
+ * @param outputBaseDir
+ * @return
+ */
+ private static File getOutputDir(final String inputName,
+ final File outputBaseDir) {
+ String name = inputName + "-" + System.currentTimeMillis() + "-split";
+ File rval = new File(outputBaseDir, name);
+ if (rval.exists()) {
+ log.warn("BUFR splitter output directory already exists, is file "
+ + inputName + " being processed twice?");
+ }
+ return rval;
+ }
+
+}
diff --git a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrParser.java b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrParser.java
index 00365c204d..bb754eab7d 100644
--- a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrParser.java
+++ b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrParser.java
@@ -20,11 +20,11 @@
package com.raytheon.uf.common.nc.bufr;
import java.io.File;
-import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
import java.util.Stack;
import ucar.ma2.Array;
@@ -37,8 +37,6 @@ import ucar.nc2.Attribute;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Structure;
import ucar.nc2.Variable;
-import ucar.nc2.iosp.bufr.writer.BufrSplitter;
-import ucar.nc2.iosp.bufr.writer.BufrSplitter.Options;
import com.raytheon.uf.common.numeric.UnsignedNumbers;
import com.raytheon.uf.common.status.IUFStatusHandler;
@@ -58,6 +56,8 @@ import com.raytheon.uf.common.status.UFStatus;
* ------------ ---------- ----------- --------------------------
* Mar 18, 2014 2905 bclement Initial creation
* Mar 26, 2014 2905 bclement fixed types, added scale/offset
+ * Apr 01, 2014 2905 bclement moved splitter functionality to separate utility
+ * added scanForStructField()
*
*
*
@@ -79,30 +79,11 @@ public class BufrParser {
private static final IUFStatusHandler log = UFStatus
.getHandler(BufrParser.class);
- private final static File DEFAULT_TMP_DIR;
+ private final File bufrFile;
- static {
- final String edexHomeProp = "edex.home";
- String baseDir = System.getProperty(edexHomeProp);
- if (baseDir == null || baseDir.trim().isEmpty()) {
- log.warn("Property '" + edexHomeProp
- + "' not set, defaulting to system tmp directory");
- DEFAULT_TMP_DIR = new File(System.getProperty("java.io.tmpdir"));
- } else {
- DEFAULT_TMP_DIR = new File(baseDir + File.separator + "data",
- "processing");
- }
- }
+ private final NetcdfFile ncfile;
- private final Options options;
-
- private final File[] splitFiles;
-
- private int fileIndex = 0;
-
- private NetcdfFile currentNcfile;
-
- private Iterator varIter;
+ private final Iterator varIter;
private Variable currentVar;
@@ -118,59 +99,9 @@ public class BufrParser {
* @throws IOException
*/
public BufrParser(final File bufrFile) throws IOException {
- this(bufrFile, DEFAULT_TMP_DIR);
- }
-
- /**
- * @param bufrFile
- * BUFR file, may contain mixed message types
- * @param outputBaseDir
- * base directory for temporary storage of split files
- * @throws IOException
- */
- public BufrParser(final File bufrFile, final File outputBaseDir)
- throws IOException {
- final String inputFile = bufrFile.getAbsolutePath();
- final File outputDir = getOutputDir(bufrFile.getName(), outputBaseDir);
- options = new Options() {
-
- @Override
- public String getFileSpec() {
- return inputFile;
- }
-
- @Override
- public String getDirOut() {
- return outputDir.getAbsolutePath();
- }
- };
-
- BufrSplitter splitter = new BufrSplitter(options);
- splitter.execute();
-
- splitFiles = outputDir.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return name.endsWith(".bufr");
- }
- });
- }
-
- /**
- * Create a temporary output directory based on the input file name
- *
- * @param inputName
- * @param outputBaseDir
- * @return
- */
- private static File getOutputDir(final String inputName,
- final File outputBaseDir) {
- String name = inputName + "-" + System.currentTimeMillis() + "-split";
- File rval = new File(outputBaseDir, name);
- if (rval.exists()) {
- log.warn("BUFR splitter output directory already exists, is a file being processed twice?");
- }
- return rval;
+ this.bufrFile = bufrFile;
+ this.ncfile = NetcdfFile.open(bufrFile.getAbsolutePath());
+ this.varIter = ncfile.getVariables().iterator();
}
/**
@@ -178,6 +109,10 @@ public class BufrParser {
* @throws IOException
*/
public boolean hasNext() throws IOException {
+ if (lastEvent == null) {
+ /* we haven't started the file yet */
+ return true;
+ }
if (!structStack.isEmpty()) {
StructureLevel level = structStack.peek();
if (level.hasNext()) {
@@ -190,11 +125,8 @@ public class BufrParser {
if (varIter != null && varIter.hasNext()) {
return true;
}
- if (fileIndex < splitFiles.length) {
- return true;
- }
if (lastEvent != null && !lastEvent.equals(Event.END_FILE)) {
- /* only one more event left, the end of the last file */
+ /* only one more event left, the end of the file */
return true;
}
return false;
@@ -209,7 +141,9 @@ public class BufrParser {
*/
public Event next() throws IOException {
Event rval;
- if (!structStack.isEmpty()) {
+ if (lastEvent == null) {
+ rval = Event.START_FILE;
+ } else if (!structStack.isEmpty()) {
rval = nextMember();
} else if (structIter != null && structIter.hasNext()) {
/* in a variable with a sequence of structures, get the next one */
@@ -229,14 +163,8 @@ public class BufrParser {
/* no more variables, we are at the end of the bufr file */
structIter = null;
currentVar = null;
- varIter = null;
- rval = endFile();
+ rval = Event.END_FILE;
}
- } else if (fileIndex < splitFiles.length) {
- /* start the next bufr file */
- rval = startFile();
- } else if (lastEvent != null && !lastEvent.equals(Event.END_FILE)) {
- rval = endFile();
} else {
/* don't set rval to null so we preserve the correct lastEvent */
return null;
@@ -323,10 +251,9 @@ public class BufrParser {
log.error("Structure variable members out of sync");
throw new IllegalStateException("Structure variable members out of sync");
}
- Iterator memberVarIter = ((Structure) parentVar)
- .getVariables().iterator();
+ List memberVars = ((Structure) parentVar).getVariables();
return startStructure(new StructureLevel(childData, childMembers,
- memberVarIter));
+ memberVars));
}
/**
@@ -340,8 +267,7 @@ public class BufrParser {
*/
private Event startStructure(Structure s, StructureData structData)
throws IOException {
- StructureLevel level = new StructureLevel(structData, s.getVariables()
- .iterator());
+ StructureLevel level = new StructureLevel(structData, s.getVariables());
return startStructure(level);
}
@@ -400,49 +326,17 @@ public class BufrParser {
}
/**
- * Start processing the next NetCDF file
- *
- * @return
- * @throws IOException
+ * @return the NetCDF File
*/
- private Event startFile() throws IOException {
- File f = splitFiles[fileIndex];
- fileIndex += 1;
- currentNcfile = NetcdfFile.open(f.getAbsolutePath());
- varIter = currentNcfile.getVariables().iterator();
- return Event.START_FILE;
+ public NetcdfFile getNcfile() {
+ return ncfile;
}
/**
- * Finalize processing of NetCDF file
- *
- * @return
- * @throws IOException
+ * @return BUFR file being processed
*/
- private Event endFile() throws IOException {
- if (currentNcfile != null) {
- currentNcfile.close();
- currentNcfile = null;
- }
- return Event.END_FILE;
- }
-
- /**
- * @return null if no file is currently being processed
- */
- public NetcdfFile getCurrentNcfile() {
- return currentNcfile;
- }
-
- /**
- * @return null if no file has started being processed
- */
- public File getCurrentFile() {
- if (fileIndex < splitFiles.length) {
- return splitFiles[fileIndex];
- } else {
- return null;
- }
+ public File getFile() {
+ return bufrFile;
}
/**
@@ -534,7 +428,7 @@ public class BufrParser {
* @param var
* @return
*/
- private DataType getUnscaledDataType(Variable var) {
+ private static DataType getUnscaledDataType(Variable var) {
DataType rval;
/*
* We will promote unsigned values to the next largest signed type
@@ -566,7 +460,7 @@ public class BufrParser {
* @param var
* @return true if the field has a scale factor or addition offset
*/
- private boolean isScaledOrOffset(Variable var) {
+ private static boolean isScaledOrOffset(Variable var) {
return var.findAttribute(OFFSET_ATTRIB) != null
|| var.findAttribute(SCALE_FACTOR_ATTRIB) != null;
}
@@ -583,6 +477,22 @@ public class BufrParser {
if (typedArray == null) {
return null;
}
+ Variable var = getFieldVariable();
+ return getFieldScalarValue(typedArray, var, charArrayAsString);
+ }
+
+ /**
+ * @param typedArray
+ * storage for field value
+ * @param var
+ * NetCDF variable
+ * @param charArrayAsString
+ * true if character arrays should be treated as strings
+ * @return null if value is a missing value
+ * @throws IOException
+ */
+ private static Object getFieldScalarValue(TypedArray typedArray,
+ Variable var, boolean charArrayAsString) {
Array array = typedArray.array;
DataType type = typedArray.type;
Object value;
@@ -598,23 +508,23 @@ public class BufrParser {
value = array.getObject(0);
}
- return processValue(value);
+ return processValue(value, var);
}
/**
* Perform any promotion, scaling or missing value operations
*
* @param value
+ * @param var
* @return
*/
- private Object processValue(Object value) {
- Variable var = getFieldVariable();
- Object rval = promoteValueType(var, value);
- if (isMissingValue(var, rval)) {
+ private static Object processValue(Object value, Variable var) {
+ Object rval = promoteValueType(value, var);
+ if (isMissingValue(rval, var)) {
rval = null;
} else if (isScaledOrOffset(var)) {
if (value instanceof Number) {
- rval = scaleAndOffset((Number) rval);
+ rval = scaleAndOffset((Number) rval, var);
} else {
log.warn("Scale or offset attribute on non-numerical field: "
+ var.getFullName());
@@ -626,11 +536,11 @@ public class BufrParser {
/**
* Promote unsigned numbers to next largest data type if needed
*
- * @param var
* @param value
+ * @param var
* @return
*/
- private Object promoteValueType(Variable var, Object value) {
+ private static Object promoteValueType(Object value, Variable var) {
if (value == null) {
return null;
}
@@ -660,14 +570,15 @@ public class BufrParser {
* Apply scale factor or addition offset if present
*
* @param value
+ * @param var
* @return
*/
- public Number scaleAndOffset(Number value) {
- Number scaleFactor = getScaleFactor();
+ public static Number scaleAndOffset(Number value, Variable var) {
+ Number scaleFactor = getFieldAttributeAsNum(SCALE_FACTOR_ATTRIB, var);
if (scaleFactor != null) {
value = value.doubleValue() * scaleFactor.doubleValue();
}
- Number offset = getOffset();
+ Number offset = getFieldAttributeAsNum(OFFSET_ATTRIB, var);
if (offset != null) {
value = value.doubleValue() + offset.doubleValue();
}
@@ -687,11 +598,12 @@ public class BufrParser {
if (typedArray == null) {
return null;
}
+ Variable var = getFieldVariable();
Array array = typedArray.array;
int len = (int) array.getSize();
Collection rval = new ArrayList(len);
for (int i = 0; i < len; ++i) {
- rval.add(processValue(array.getObject(i)));
+ rval.add(processValue(array.getObject(i), var));
}
return rval;
}
@@ -777,6 +689,22 @@ public class BufrParser {
return rval;
}
+ /**
+ * Get attribute value for field from variable
+ *
+ * @param name
+ * @param var
+ * @return
+ */
+ private static Number getFieldAttributeAsNum(String name, Variable var) {
+ Number rval = null;
+ Attribute attr = var.findAttributeIgnoreCase(name);
+ if (attr != null) {
+ rval = attr.getNumericValue();
+ }
+ return rval;
+ }
+
/**
* Get variable object for current field
*
@@ -794,17 +722,18 @@ public class BufrParser {
}
/**
- * @param var
* @param unscaledValue
* field value before any scaling or offset is applied
+ * @param var
* @return true if value matches the missing value for field
*/
- private boolean isMissingValue(Variable var, Object unscaledValue) {
+ private static boolean isMissingValue(Object unscaledValue, Variable var) {
if (unscaledValue == null) {
return true;
}
boolean rval;
- Attribute missingAttrib = getFieldAttribute(MISSING_VAL_ATTRIB);
+ Attribute missingAttrib = var
+ .findAttributeIgnoreCase(MISSING_VAL_ATTRIB);
if (missingAttrib == null) {
/* if there is no special missing value, all values are valid */
rval = false;
@@ -851,19 +780,41 @@ public class BufrParser {
}
/**
- * clean up temporary files
+ * Get field from current structure level. Does not affect the current state
+ * of the parser. Only searches current level (does not go into
+ * substructures).
+ *
+ * @param fieldName
+ * @param charArrayAsString
+ * @return null if no field found or parser is not currently parsing a
+ * structure
*/
- public void clean() {
- for (File f : splitFiles) {
- if (!f.delete()) {
- log.error("Unable to delete temporary file: "
- + f.getAbsolutePath());
+ public BufrDataItem scanForStructField(String fieldName, boolean charArrayAsString) {
+ BufrDataItem rval = null;
+ if ( structStack.isEmpty()){
+ return rval;
+ }
+ StructureLevel level = structStack.peek();
+ Iterator memberIter = level.getMemberList().iterator();
+ Iterator varIter = level.getMemberVarList().iterator();
+ while (memberIter.hasNext() && varIter.hasNext()) {
+ Member member = memberIter.next();
+ Variable variable = varIter.next();
+ DataType type = member.getDataType();
+ if (!type.equals(DataType.STRUCTURE) && !type.equals(DataType.SEQUENCE)) {
+ /* current member is a field */
+ if (member.getName().equals(fieldName)){
+ StructureData sd = level.getStructData();
+ Array array = sd.getArray(member);
+ Object value = getFieldScalarValue(new TypedArray(array,
+ type), variable, charArrayAsString);
+ rval = new BufrDataItem(member.getName(), value, type,
+ variable);
+ break;
+ }
}
}
- File outdir = new File(options.getDirOut());
- if (!outdir.delete()) {
- log.error("Unable to delete temporary directory: "
- + outdir.getAbsolutePath());
- }
+ return rval;
}
+
}
diff --git a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/StructureLevel.java b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/StructureLevel.java
index 3d363f49c8..1bbac68136 100644
--- a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/StructureLevel.java
+++ b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/StructureLevel.java
@@ -20,6 +20,7 @@
package com.raytheon.uf.common.nc.bufr;
import java.util.Iterator;
+import java.util.List;
import ucar.ma2.ArraySequence;
import ucar.ma2.StructureData;
@@ -40,6 +41,7 @@ import com.raytheon.uf.common.status.UFStatus;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 25, 2014 2905 bclement Initial creation
+ * Apr 01, 2014 2905 bclement added references to member and variable lists
*
*
*
@@ -53,8 +55,12 @@ public class StructureLevel {
private final StructureData structData;
+ private final List memberList;
+
private final Iterator memberIter;
+ private final List memberVarList;
+
private final Iterator memberVarIter;
private Member currentMember;
@@ -66,8 +72,8 @@ public class StructureLevel {
* @param memberVarIter
*/
public StructureLevel(StructureData structData,
- Iterator memberVarIter) {
- this(structData, structData.getStructureMembers(), memberVarIter);
+ List memberVariables) {
+ this(structData, structData.getStructureMembers(), memberVariables);
}
/**
@@ -76,10 +82,12 @@ public class StructureLevel {
* @param memberVarIter
*/
public StructureLevel(StructureData structData, StructureMembers members,
- Iterator memberVarIter) {
+ List memberVariables) {
this.structData = structData;
- this.memberIter = members.getMembers().iterator();
- this.memberVarIter = memberVarIter;
+ this.memberList = members.getMembers();
+ this.memberIter = memberList.iterator();
+ this.memberVarList = memberVariables;
+ this.memberVarIter = memberVarList.iterator();
}
/**
@@ -126,17 +134,17 @@ public class StructureLevel {
}
/**
- * @return the memberIter
+ * @return the memberList
*/
- public Iterator getMemberIter() {
- return memberIter;
+ public List getMemberList() {
+ return memberList;
}
/**
- * @return the memberVarIter
+ * @return the memberVarList
*/
- public Iterator getMemberVarIter() {
- return memberVarIter;
+ public List getMemberVarList() {
+ return memberVarList;
}
/**