/* * #%L * OME Bio-Formats package for reading and converting biological file formats. * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. * #L% */ package loci.formats.services; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import loci.common.Constants; import loci.common.Location; import loci.common.services.AbstractService; import loci.common.services.ServiceException; import ucar.ma2.Array; import ucar.ma2.InvalidRangeException; import ucar.nc2.Attribute; import ucar.nc2.Group; import ucar.nc2.NetcdfFile; import ucar.nc2.Variable; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoSerializable; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** * Utility class for working with NetCDF/HDF files. Uses reflection to * call the NetCDF Java library. */ public class NetCDFServiceImpl extends AbstractService implements NetCDFService, KryoSerializable { // -- Constants -- public static final String NO_NETCDF_MSG = "NetCDF is required to read NetCDF/HDF variants. " + "Please obtain the necessary JAR files from " + "http://www.openmicroscopy.org/site/support/bio-formats/developers/java-library.html.\n" + "Required JAR files are netcdf-4.3.19.jar and slf4j-jdk14.jar."; // -- Fields -- private String currentFile; private Vector<String> attributeList; private Vector<String> variableList; /** NetCDF file instance. */ private NetcdfFile netCDFFile; /** Root of the NetCDF file. */ private Group root; // -- NetCDFService API methods --- /** * Default constructor. */ public NetCDFServiceImpl() { // One check from each package checkClassDependency(ucar.nc2.Attribute.class); checkClassDependency(ucar.ma2.Array.class); } /* (non-Javadoc) * @see loci.formats.NetCDFService#setFile(java.lang.String) */ @Override public void setFile(String file) throws IOException { this.currentFile = file; init(); attributeList = new Vector<String>(); variableList = new Vector<String>(); List<Group> groups = new ArrayList<Group>(); groups.add(root); parseAttributesAndVariables(groups); } /* (non-Javadoc) * @see loci.formats.NetCDFService#getFile() */ @Override public String getFile() { return currentFile; } /* (non-Javadoc) * @see loci.formats.NetCDFService#getAttributeList() */ @Override public Vector<String> getAttributeList() { return attributeList; } /* (non-Javadoc) * @see loci.formats.NetCDFService#getVariableList() */ @Override public Vector<String> getVariableList() { return variableList; } /* (non-Javadoc) * @see loci.formats.NetCDFService#getAttributeValue(java.lang.String) */ @Override public String getAttributeValue(String path) { String groupName = getDirectory(path); String attributeName = getName(path); Group group = getGroup(groupName); Attribute attribute = group.findAttribute(attributeName); if (attribute == null) return null; return arrayToString(attribute.getValues()); } /* (non-Javadoc) * @see loci.formats.NetCDFService#getVariableValue(java.lang.String) */ @Override public Object getVariableValue(String name) throws ServiceException { return getArray(name, null, null); } /* (non-Javadoc) * @see loci.formats.NetCDFService#getArray(java.lang.String, int[], int[]) */ @Override public Object getArray(String path, int[] origin, int[] shape) throws ServiceException { String groupName = getDirectory(path); String variableName = getName(path); Group group = getGroup(groupName); Variable variable = group.findVariable(variableName); try { if (origin != null && shape != null) { return variable.read(origin, shape).reduce().copyToNDJavaArray(); } return variable.read().copyToNDJavaArray(); } catch (InvalidRangeException e) { throw new ServiceException(e); } catch (IOException e) { throw new ServiceException(e); } catch (NullPointerException e) { return null; } } /* (non-Javadoc) * @see loci.formats.NetCDFService#getVariableAttributes(java.lang.String) */ @Override public Hashtable<String, Object> getVariableAttributes(String name) { String groupName = getDirectory(name); String variableName = getName(name); Group group = getGroup(groupName); Variable variable = group.findVariable(variableName); Hashtable<String, Object> toReturn = new Hashtable<String, Object>(); if (variable != null) { List<Attribute> attributes = variable.getAttributes(); for (Attribute attribute: attributes) { toReturn.put(attribute.getName(), arrayToString(attribute.getValues())); } } return toReturn; } @Override public int getDimension(String name) { String groupName = getDirectory(name); String variableName = getName(name); Group group = getGroup(groupName); return group.findDimension(variableName).getLength(); } /* (non-Javadoc) * @see loci.formats.NetCDFService#close() */ @Override public void close() throws IOException { if (netCDFFile != null) netCDFFile.close(); currentFile = null; attributeList = null; variableList = null; netCDFFile = null; root = null; } // -- Helper methods -- /** * Recursively parses attribute and variable paths, filling * <code>attributeList</code> and <code>variableList</code>. * @param groups List of groups to recursively parse. */ private void parseAttributesAndVariables(List<Group> groups) { for (Group group : groups) { String groupName = group.getName(); List<Attribute> attributes = group.getAttributes(); for (Attribute attribute : attributes) { String attributeName = attribute.getName(); if (!groupName.endsWith("/")) attributeName = "/" + attributeName; attributeList.add(groupName + attributeName); } List<Variable> variables = group.getVariables(); for (Variable variable : variables) { String variableName = variable.getName(); if (!groupName.endsWith("/")) variableName = "/" + variableName; variableList.add(variableName); } groups = group.getGroups(); parseAttributesAndVariables(groups); } } /** * Retrieves a group based on its fully qualified path. * @param path Fully qualified path to the group. * @return Group or <code>root</code> if the group cannot be found. */ private Group getGroup(String path) { if (path.indexOf("/") == -1) { return root; } StringTokenizer tokens = new StringTokenizer(path, "/"); Group parent = root; while (tokens.hasMoreTokens()) { String token = tokens.nextToken(); Group nextParent = parent.findGroup(token); if (nextParent == null) { break; } parent = nextParent; } return parent == null ? root : parent; } private String getDirectory(String path) { return path.substring(0, path.lastIndexOf("/")); } private String getName(String path) { return path.substring(path.lastIndexOf("/") + 1); } private String arrayToString(Array values) { Object v = values.copyTo1DJavaArray(); if (v instanceof Object[]) { Object[] array = (Object[]) v; StringBuffer sb = new StringBuffer(); for (int i = 0; i < array.length; i++) { sb.append((String) array[i]); } return sb.toString().trim(); } return values.toString().trim(); } private void init() throws IOException { String currentId = Location.getMappedId(currentFile); PrintStream outStream = System.out; PrintStream throwaway = new PrintStream( new ByteArrayOutputStream(), false /*auto-flush*/, Constants.ENCODING) { @Override public void print(String s) { } }; System.setOut(throwaway); throwaway.close(); netCDFFile = NetcdfFile.open(currentId); System.setOut(outStream); root = netCDFFile.getRootGroup(); } // -- KryoSerializable methods -- @Override public void read(Kryo kryo, Input in) { currentFile = kryo.readObjectOrNull(in, String.class); attributeList = kryo.readObjectOrNull(in, Vector.class); variableList = kryo.readObjectOrNull(in, Vector.class); try { init(); } catch (IOException e) { } } @Override public void write(Kryo kryo, Output out) { kryo.writeObjectOrNull(out, currentFile, String.class); kryo.writeObjectOrNull(out, attributeList, Vector.class); kryo.writeObjectOrNull(out, variableList, Vector.class); } }