gt: 			int     pos  = -1;
context: /*-
   *******************************************************************************
   * Copyright (c) 2011, 2014 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 Gerring - initial API and implementation and/or initial documentation
   *******************************************************************************/ 
  package org.eclipse.dawnsci.hdf5.nexus;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Queue;
  import java.util.TreeMap;
  
  import ncsa.hdf.object.Attribute;
  import ncsa.hdf.object.Dataset;
  import ncsa.hdf.object.Datatype;
  import ncsa.hdf.object.FileFormat;
  import ncsa.hdf.object.Group;
  import ncsa.hdf.object.HObject;
  import ncsa.hdf.object.h5.H5Datatype;
  
  import org.eclipse.dawnsci.analysis.api.dataset.IDataset;
  import org.eclipse.dawnsci.hdf5.H5Utils;
  import org.eclipse.dawnsci.hdf5.HierarchicalDataFactory;
  import org.eclipse.dawnsci.hdf5.IFileFormatDataFile;
  import org.eclipse.dawnsci.hdf5.IHierarchicalDataFile;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * Class used to mark groups in the hdf5 tree with nexus attributes.
   * 
   * This is a way not to use the nexus API.
   * 
   * @author gerring
   *
   */
  public class NexusUtils {
  
  	public static final String NXCLASS = "NX_class";	
  	public static final String AXIS    = "axis";	
  	public static final String LABEL   = "label";	
  	public static final String PRIM    = "primary";	
  	public static final String SIGNAL  = "signal";	
  	public static final String UNIT    = "units";	
  	
  	private final static Logger logger = LoggerFactory.getLogger(NexusUtils.class);
  
  	/**
  	 * Sets the nexus attribute so that if something is looking for them,
  	 * then they are there.
  	 * 
  	 * @param file
  	 * @param entry
  	 * @param entryKey
  	 * @throws Exception
  	 */
  	public static void setNexusAttribute(final FileFormat file, 
  			                             final HObject    entry,
  			                             final String     entryKey) throws Exception {
  		
  		setAttribute(file, entry, NXCLASS, entryKey);
  	}
  	
  	/**
  	 * Set any attribute on a HObject.
  	 * 
  	 * @param file
  	 * @param entry
  	 * @param name
  	 * @param entryKey
  	 * @throws Exception
  	 */
  	public static void setAttribute(final FileFormat file, 
  									final HObject    entry,
  									final String     name,
  									final String     entryKey) throws Exception {
  		setAttribute(file, entry, name, entryKey, ATTRIBUTE_TYPE.FORCE);
  	}
  	
  	public enum ATTRIBUTE_TYPE {
  		/**
  		 * Create if necessary or overwrite
  		 */
  		FORCE,
  		/**
  		 * Do not create but overwrite if exists
  		 */
  		OVERWRITE,
  		/**
  		 * Do not update if exists and is equal, otherwise create
  		 */
  		NO_OVERWRITE;
  	}
  
  	public static void setAttribute(final FileFormat file,
  			final HObject    entry,
  			final String     name,
  			final String     entryKey,
  			final ATTRIBUTE_TYPE type) throws Exception {
  		
  		boolean attributeExist = false;
  		// Check if attribute is already there
  		@SuppressWarnings("unchecked")
  		final List<Object> attrList = entry.getMetadata();
  		if (attrList!=null) {
  			LOOP: for (Object object : attrList) {
  				if (object instanceof Attribute) {
  					final Attribute a    = (Attribute)object;
  					if (name.equals(a.getName())) {
  						
  						final Object  oValue = a.getValue();
  						final String[] aValue = oValue instanceof String[] ? (String[])oValue : null;
  						if (aValue!=null && type==ATTRIBUTE_TYPE.NO_OVERWRITE && entryKey.equals(aValue[0])) {
  							return;
  						}
  						attributeExist = true;
  						if (type == ATTRIBUTE_TYPE.OVERWRITE) {
  							//Seems to be needed if you are overwriting a shorter attribute string
  							// to not get truncation
  							final long id = entry.open();
  							try {
  								entry.removeMetadata(a);
  								attrList.remove(a);
  								String[] classValue = {entryKey};
  								Datatype attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length()+1, -1, -1);
  								Attribute attr = new Attribute(name, attrType, new long[]{1});
  								attr.setValue(classValue);
  								entry.writeMetadata(attr);
  								
  							} finally {
  								entry.close(id);
  							}
  							return;
  						}
  						
  						break LOOP;
  					}
  				}
  			}
  		}
  		if (type==ATTRIBUTE_TYPE.OVERWRITE & !attributeExist) {
  			return;
  		}
  		final long id = entry.open();
  		try {
  			String[] classValue = {entryKey};
  			Datatype attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length()+1, -1, -1);
  			Attribute attr = new Attribute(name, attrType, new long[]{1});
  			attr.setValue(classValue);
  			
  			file.writeAttribute(entry, attr, attributeExist);
  			
  			if (entry instanceof Group) {
  				attrList.add(attr);
  				((Group)entry).writeMetadata(attrList);
  			} else if (entry instanceof Dataset) {
  				attrList.add(attr);
  				((Dataset)entry).writeMetadata(attrList);
  			}
  
  
  		} finally {
  			entry.close(id);
  		}
  	}
  
  
  	/**
  	 * Does not replace the attribute if it exists
  	 * @param file
  	 * @param entry
  	 * @param name
  	 * @param value
  	 * @throws Exception
  	 */
  	public static void setIntAttribute(final FileFormat file, 
  							           final HObject   entry,
  							           final String    name,
  							           final int       value) throws Exception {
  
  		@SuppressWarnings("unchecked")
  		final List<Object> attrList = entry.getMetadata();
  		if (attrList!=null) for (Object object : attrList) {
  			if (object instanceof Attribute) {
  				final Attribute a      = (Attribute)object;
  				if (name.equals(a.getName())) return;
  			}
  		}
  		
  		final long id = entry.open();
  		try {
  	        Datatype attrType = new H5Datatype(Datatype.CLASS_INTEGER, 1, -1, -1);
  	        Attribute attr = new Attribute(name, attrType, new long[]{1});
  	        attr.setValue(new int[]{value});
  			
  	        file.writeAttribute(entry, attr, false);
  
  	        if (entry instanceof Group) {
  	        	attrList.add(attr);
  				((Group)entry).writeMetadata(attrList);
  	        } else if (entry instanceof Dataset) {
  	        	attrList.add(attr);
  				((Dataset)entry).writeMetadata(attrList);
  	        }
  		        
  		    
  		} finally {
  			entry.close(id);
  		}
  	}
  	
  	public static void setDatasetAttribute(IDataset dataset, String dsPath, IHierarchicalDataFile file) throws Exception {
  		setDatasetAttribute(dataset, (Dataset)file.getData(dsPath), file);
  	}
  
  	public static void setDatasetAttribute(IDataset dataset, Dataset ds, IHierarchicalDataFile file) throws Exception {
  		setDatasetAttribute(((IFileFormatDataFile)file).getFileFormat(), 
  				                      ds, 
  				                      dataset.getName(), 
  				                      H5Utils.getDatatype(dataset), 
  				                      H5Utils.getLong(dataset.getShape()), 
  				                      ((org.eclipse.dawnsci.analysis.dataset.impl.Dataset)dataset).getBuffer());
  	}
  
  	/**
  	 * Does not replace the attribute if it exists
  	 * @param file
  	 * @param entry
  	 * @param name
  	 * @param value
  	 * @throws Exception
  	 */
  	public static void setDatasetAttribute(final FileFormat file, 
  							           final HObject   entry,
  							           final String    name,
  							           final Datatype dtype, final long[] shape, final Object buffer) throws Exception {
  		
  		@SuppressWarnings("unchecked")
  		final List<Object> attrList = entry.getMetadata();
  		if (attrList!=null) for (Object object : attrList) {
  			if (object instanceof Attribute) {
  				final Attribute a      = (Attribute)object;
  				if (name.equals(a.getName())) return;
  			}
  		}
  		
  		final long id = entry.open();
  		try {
  	        Attribute attr = new Attribute(name, dtype, shape);
  	        attr.setValue(buffer);
  			
  	        file.writeAttribute(entry, attr, false);
  
  	        if (entry instanceof Group) {
  	        	attrList.add(attr);
  				((Group)entry).writeMetadata(attrList);
  	        } else if (entry instanceof Dataset) {
  	        	attrList.add(attr);
  				((Dataset)entry).writeMetadata(attrList);
  	        }
  		        
  		    
  		} finally {
  			entry.close(id);
  		}
  	}
  	
  	
  	/**
  	 * Gets the nexus axes from the data node, if there are any there
  	 * 
  	 * TODO Deal with label attribute?
  	 * 
  	 * @param FileFormat - the file
  	 * @param dataNode - the node with the signal
  	 * @param dimension, we want the axis for 1, 2, 3 etc.
  	 * @return
  	 * @throws Exception especially if dims are ask for which the signal does not have.
  	 */
  	public static List<String> getAxes(final FileFormat file, final Dataset signal, int dimension) throws Exception {
  		
  		final List<String>         axesTmp = new ArrayList<String>(3);
          final Map<Integer, String> axesMap = new TreeMap<Integer, String>();
  		
          signal.getMetadata();
         
          if (dimension>signal.getDims().length) return null;
          final long size = signal.getDims()[dimension-1];
  
          final String parentPath = signal.getFullName().substring(0, signal.getFullName().lastIndexOf("/"));
          
          final Group parent = (Group)file.get(parentPath);
          
          final List<HObject> children = parent.getMemberList();
  		for (HObject hObject : children) {
  			final List<?> att = hObject.getMetadata();
  			if (!(hObject instanceof Dataset)) continue;
  			if (hObject.getFullName().equals(signal.getFullName())) continue;
  			
  			String axis = null;
  