Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*-
*******************************************************************************
* Copyright (c) 2015 Diamond Light Source Ltd.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthew Dickie - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.dawnsci.nexus.builder.data.impl;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.stream.IntStream;
import org.eclipse.dawnsci.analysis.api.tree.Attribute;
import org.eclipse.dawnsci.analysis.api.tree.DataNode;
import org.eclipse.dawnsci.analysis.api.tree.GroupNode;
import org.eclipse.dawnsci.analysis.api.tree.Node;
import org.eclipse.dawnsci.analysis.api.tree.SymbolicNode;
import org.eclipse.dawnsci.analysis.tree.TreeFactory;
import org.eclipse.dawnsci.nexus.NXdata;
import org.eclipse.dawnsci.nexus.NXentry;
import org.eclipse.dawnsci.nexus.NXobject;
import org.eclipse.dawnsci.nexus.NexusException;
import org.eclipse.dawnsci.nexus.NexusNodeFactory;
import org.eclipse.dawnsci.nexus.builder.NexusEntryBuilder;
import org.eclipse.dawnsci.nexus.builder.NexusObjectProvider;
import org.eclipse.dawnsci.nexus.builder.data.AxisDataDevice;
import org.eclipse.dawnsci.nexus.builder.data.DataDevice;
import org.eclipse.dawnsci.nexus.builder.data.DataDeviceBuilder;
import org.eclipse.dawnsci.nexus.builder.data.NexusDataBuilder;
import org.eclipse.dawnsci.nexus.builder.data.PrimaryDataDevice;
import org.eclipse.dawnsci.nexus.builder.impl.DefaultNexusEntryBuilder;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.IntegerDataset;
import org.eclipse.january.dataset.StringDataset;
/**
* Default implementation of {@link NexusDataBuilder}.
*
*/
public class DefaultNexusDataBuilder extends AbstractNexusDataBuilder implements NexusDataBuilder {
private int signalFieldRank;
private Node signalNode = null;
private StringDataset dimensionDefaultAxisNames;
private String signalFieldSourceName;
private String signalFieldDestName;
/**
* Create a new {@link DefaultNexusDataBuilder}. This constructor should only be
* called by {@link DefaultNexusEntryBuilder}.
* @param entryBuilder parent entry builder
* @param nxData {@link NXdata} object to wrap
*/
public DefaultNexusDataBuilder(NexusEntryBuilder entryBuilder, final NXdata nxData) {
super(entryBuilder, nxData);
}
/* (non-Javadoc)
* @see org.eclipse.dawnsci.nexus.builder.NexusDataBuilder#getNexusData()
*/
@Override
public NXdata getNxData() {
return nxData;
}
private boolean isPrimaryDeviceAdded() {
return signalNode != null;
}
/* (non-Javadoc)
* @see org.eclipse.dawnsci.nexus.builder.NexusDataBuilder#setPrimaryDevice(org.eclipse.dawnsci.nexus.builder.DataDevice.PrimaryDataDevice)
*/
@Override
public void setPrimaryDevice(PrimaryDataDevice primaryDataDevice)
throws NexusException {
addSignalAndAxesAttributes(primaryDataDevice);
addDevice(primaryDataDevice, true);
}
/* (non-Javadoc)
* @see org.eclipse.dawnsci.nexus.builder.NexusDataBuilder#addDataDevice(org.eclipse.dawnsci.nexus.builder.NexusObjectProvider, java.lang.Integer, int[])
*/
@Override
public void addAxisDevice(NexusObjectProvider dataDevice,
Integer defaultAxisDimension, int... dimensionMappings) throws NexusException {
DataDeviceBuilder builder = DataDeviceBuilder.newAxisDataDeviceBuilder(dataDevice, defaultAxisDimension);
builder.setDefaultDimensionMappings(dimensionMappings);
addAxisDevice((AxisDataDevice) builder.build());
}
/* (non-Javadoc)
* @see org.eclipse.dawnsci.nexus.builder.NexusDataBuilder#addDataDevice(org.eclipse.dawnsci.nexus.builder.DataDevice.AxisDataDevice)
*/
@Override
public void addAxisDevice(AxisDataDevice axisDataDevice) throws NexusException {
if (!isPrimaryDeviceAdded()) {
throw new IllegalStateException("The primary device has not been set.");
}
addDevice(axisDataDevice, false);
}
/**
* Adds the data fields for the given device to the {@link NXdata}
* @param dataDevice data device, wrapping an {@link NexusObjectProvider}
* @param isPrimary true if this is the primary device, false otherwise
* @throws NexusException
*/
private void addDevice(DataDevice dataDevice, boolean isPrimary) throws NexusException {
String targetPrefix = getPath(dataDevice.getNexusObject());
// if this is the primary device, add the signal field
if (isPrimary) {
addDataField(dataDevice, signalFieldSourceName, targetPrefix);
}
// add the axis fields for this device
for (String sourceFieldName : dataDevice.getAxisFieldNames()) {
addDataField(dataDevice, sourceFieldName, targetPrefix);
}
}
/**
* Returns the path of the given nexus object within the nexus tree.
* @param nexusObject
* @return path of the nexus object
*/
private String getPath(N nexusObject) {
NXentry nxEntry = entryBuilder.getNXentry();
String entryName = entryBuilder.getEntryName();
String subPath = getRelativePath(nxEntry, nexusObject);
if (subPath != null) {
return GroupNode.SEPARATOR + entryName + GroupNode.SEPARATOR + subPath;
}
return null;
}
private String getRelativePath(GroupNode groupToSearch, GroupNode groupToFind) {
Iterator nodeNameIter = groupToSearch.getNodeNameIterator();
while (nodeNameIter.hasNext()) {
String nodeName = nodeNameIter.next();
if (groupToSearch.containsGroupNode(nodeName)) {
GroupNode childGroup = groupToSearch.getGroupNode(nodeName);
if (childGroup == groupToFind) {
return nodeName;
}
String subPath = getRelativePath(childGroup, groupToFind);
if (subPath != null) {
return nodeName + GroupNode.SEPARATOR + subPath;
}
}
}
return null;
}
/**
* Adds the device for the given field to the dataset for the
* @axes attribute of the {@link NXdata} group for the index with
* the given name
* @param defaultAxisDimension default axis dimension
* @param destinationFieldName destination field name
*/
private void addDeviceToDefaultAxes(int defaultAxisDimension, String destinationFieldName) {
// if this is the default axis for a dimension then update the dataset for the 'axes'
// attribute of the NXdata group
if (defaultAxisDimension < 0 || defaultAxisDimension > dimensionDefaultAxisNames.getSize() - 1) {
throw new IllegalArgumentException("Default axis dimension for device must be between 0 and " +
dimensionDefaultAxisNames.getSize() + ", was: " + defaultAxisDimension);
}
dimensionDefaultAxisNames.set(destinationFieldName, defaultAxisDimension);
}
/**
* Adds a link to the data field with the name sourceFieldName within the given
* {@link NexusObjectProvider} to the {@link NXdata} group, with the name within the
* NXdata group destinationFieldName
*
* @param nexusObjectProvider nexus object provider to get the nexus object from
* @param sourceFieldName name of field within the nexus object
* @param targetPrefix prefix of @target attribute
* @throws NexusException
*/
private void addDataField(DataDevice dataDevice,
String sourceFieldName, String targetPrefix) throws NexusException {
String destinationFieldName = dataDevice.getDestinationFieldName(sourceFieldName);
// check that there is not an existing node with the same name
if (nxData.containsDataNode(destinationFieldName)) {
throw new IllegalArgumentException("The NXdata element already contains a data node with the name: " + destinationFieldName);
}
// add the node to the nxData group
final Node fieldNode = dataDevice.getFieldNode(sourceFieldName);
addFieldNode(destinationFieldName, fieldNode);
// create the @target attribute if not already present
if (targetPrefix != null && !fieldNode.containsAttribute(ATTR_NAME_TARGET)) {
fieldNode.addAttribute(TreeFactory.createAttribute(
ATTR_NAME_TARGET, targetPrefix + GroupNode.SEPARATOR + sourceFieldName));
}
// create the @long_name attribute?
// if (!dataNode.containsAttribute(ATTR_NAME_LONG_NAME)) { // TODO check this
// dataNode.addAttribute(TreeFactory.createAttribute(ATTR_NAME_LONG_NAME, sourceFieldName));
// }
// create the @{axisname}_indices attribute
if (!destinationFieldName.equals(signalFieldDestName)) {
final Attribute axisIndicesAttribute = createAxisIndicesAttribute(dataDevice, sourceFieldName);
nxData.addAttribute(axisIndicesAttribute);
// add the axis dimension to the default axes - the @axes attribute
Integer defaultAxisDimension = dataDevice.getDefaultAxisDimension(sourceFieldName);
if (defaultAxisDimension != null) {
addDeviceToDefaultAxes(defaultAxisDimension, destinationFieldName);
}
}
}
private void addFieldNode(String destinationFieldName, Node node) {
if (node.isDataNode()) {
nxData.addDataNode(destinationFieldName, (DataNode) node);
} else if (node.isSymbolicNode()) {
// we have to copy the symbolic node as the NexusFileHDF5 cannot create a hard link
// to a symbolic node when saving the tree
SymbolicNode oldSymbolicNode = (SymbolicNode) node;
SymbolicNode newSymbolicNode = NexusNodeFactory.createSymbolicNode(
oldSymbolicNode.getSourceURI(), oldSymbolicNode.getPath());
nxData.addSymbolicNode(destinationFieldName, newSymbolicNode);
} else {
throw new IllegalArgumentException("Node must be a DataNode or SymbolicNode");
}
}
/**
* Adds the @signal and @axes attributes to the {@link NXdata} group.
* @param primaryDataDevice
* @param sourceFieldName
* @param destinationFieldName
* @throws NexusException
*/
private void addSignalAndAxesAttributes(PrimaryDataDevice primaryDataDevice) throws NexusException {
if (isPrimaryDeviceAdded()) {
throw new IllegalArgumentException("Primary device already added");
}
// get the source and destination name for the signal field
signalFieldSourceName = primaryDataDevice.getSignalFieldSourceName();
signalFieldDestName = primaryDataDevice.getDestinationFieldName(signalFieldSourceName);
final Attribute signalAttribute = TreeFactory.createAttribute(ATTR_NAME_SIGNAL, signalFieldDestName, false);
nxData.addAttribute(signalAttribute);
// create the 'axes' attribute of the NXgroup and set each axis name
// to the placeholder value "."
signalNode = primaryDataDevice.getFieldNode(signalFieldSourceName);
signalFieldRank = primaryDataDevice.getFieldRank(signalFieldSourceName);
if (signalFieldRank > 0) {
dimensionDefaultAxisNames = DatasetFactory.zeros(StringDataset.class, signalFieldRank);
dimensionDefaultAxisNames.fill(NO_DEFAULT_AXIS_PLACEHOLDER);
final Attribute axesAttribute = TreeFactory.createAttribute(ATTR_NAME_AXES, dimensionDefaultAxisNames, false);
nxData.addAttribute(axesAttribute);
}
}
private Attribute createAxisIndicesAttribute(DataDevice dataDevice, String sourceFieldName) {
// if the default axis dimension is specified and the dataset has a rank of 1,
// then this has to be the dimension mapping as well
final String destinationFieldName = dataDevice.getDestinationFieldName(sourceFieldName);
final int fieldRank = dataDevice.getFieldRank(sourceFieldName);
final Integer defaultAxisDimension = dataDevice.getDefaultAxisDimension(sourceFieldName);
int[] dimensionMappings = dataDevice.getDimensionMappings(sourceFieldName);
if (defaultAxisDimension != null && fieldRank == 1) {
dimensionMappings = new int[] { defaultAxisDimension };
}
if (dimensionMappings != null) {
if (dimensionMappings.length == 0) {
dimensionMappings = null;
} else {
// validate the dimension mappings if specified
validateDimensionMappings(sourceFieldName, dimensionMappings, fieldRank);
}
}
// create the {axisname}_indices attribute of the NXdata group for this axis device
final String attrName = destinationFieldName + ATTR_SUFFIX_INDICES;
final IntegerDataset indicesDataset = DatasetFactory.zeros(IntegerDataset.class, fieldRank);
// set the dimension mappings into the dataset, if not set use 0, 1, 2, etc...
final int[] finalDimensionMappings = dimensionMappings;
IntStream.range(0, fieldRank).forEach(i -> indicesDataset.setItem(
finalDimensionMappings == null ? i : finalDimensionMappings[i], i));
return TreeFactory.createAttribute(attrName, indicesDataset, false);
}
/**
* Validate that the given dimension mappings. The size of the array must equal the
* given rank and each value in the array must be between 0 (inclusive) and the
* rank of the signal data field
* @param sourceFieldName source field name
* @param dimensionMappings dimension mappings
* @param rank rank of the dataset to add
*/
private void validateDimensionMappings(String sourceFieldName,
int[] dimensionMappings, int rank) {
// size of dimensionMappings must equal rank of the dataset to add
if (dimensionMappings.length != rank) {
throw new IllegalArgumentException("The size of the dimension mappings array must equal the rank of the dataset for the field: " + sourceFieldName);
}
// each element of the dimensionMappings array must between 0 and the rank of the default data node of the NXdata group
for (int dimensionMapping : dimensionMappings) {
if (dimensionMapping < 0 || dimensionMapping >= signalFieldRank) {
throw new IllegalArgumentException(MessageFormat.format("Invalid dimension mapping for field ''{0}'', {1}, must be between 0 and {2} exclusive, as the rank of the primary data field ''{3}'' has rank {2}. This problem can occur when the rank of the signal data field is smaller than the rank of the scan.",
sourceFieldName, dimensionMapping, signalFieldRank, signalFieldSourceName));
}
}
}
}