/*- ******************************************************************************* * 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.hdf.object; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import hdf.object.Attribute; import hdf.object.Dataset; import hdf.object.Datatype; import hdf.object.HObject; public class HierarchicalDataUtils { /** * Overwrites destination_file if it exists, creates new if not. * * @param source_file * @param destination_file * @throws IOException */ public final static void copy(final File source_file, final File destination_file) throws IOException { HierarchicalDataUtils.copy(source_file, destination_file, new byte[4096]); } /** * Overwrites destination_file if it exists, creates new if not. * * @param source_file * @param destination_file * @param buffer * @throws IOException */ public final static void copy(final File source_file, final File destination_file, final byte[] buffer) throws IOException { if (!source_file.exists()) { return; } final File parTo = destination_file.getParentFile(); if (!parTo.exists()) { parTo.mkdirs(); } if (!destination_file.exists()) { destination_file.createNewFile(); } InputStream source = null; OutputStream destination = null; try { source = new BufferedInputStream(new FileInputStream(source_file)); destination = new BufferedOutputStream(new FileOutputStream(destination_file)); int bytes_read; while (true) { bytes_read = source.read(buffer); if (bytes_read == -1) { break; } destination.write(buffer, 0, bytes_read); } } finally { source.close(); destination.close(); } } /** * @throws Exception * @throws OutOfMemoryError * */ public static boolean isDataType(Dataset set, int requiredType) throws OutOfMemoryError, Exception { if (requiredType<0) return true; // Numbers less than 0 are any dataset final int type = set.getDatatype().getDatatypeClass(); if (type==Datatype.CLASS_FLOAT || type==Datatype.CLASS_INTEGER || type==Datatype.CLASS_CHAR) { if (IHierarchicalDataFile.NUMBER_ARRAY==requiredType) { long[]shape = getDims(set); if (shape==null) return true; return shape.length>1 || shape[0]>1; } else if (IHierarchicalDataFile.SCALAR==requiredType) { long[]shape = getDims(set); if (shape==null) return true; return shape.length==1 && shape[0]==1; } } if (type==Datatype.CLASS_STRING) { if (IHierarchicalDataFile.TEXT==requiredType) { return true; } else if (IHierarchicalDataFile.SCALAR==requiredType) { long[]shape = getDims(set); if (shape==null) return true; return shape.length==1 && shape[0]==1; } } return requiredType==type; } /** * Returns the link in another file if this is a linked file. * @param set * @param original - may be null * @return * @throws OutOfMemoryError * @throws Exception */ public static HObject getDataLink(HObject set, HierarchicalDataFile original) { try { // To make things faster, we only check for Datasets if (!(set instanceof Dataset)) return null; List<?> attributes = set.getMetadata(); // Appears in stack traces of VM exists if (attributes==null || attributes.isEmpty()) return null; for (Object attribute : attributes) { if (attribute instanceof Attribute) { Attribute a = (Attribute)attribute; if (a.getName().equals(HierarchicalInfo.NAPIMOUNT)) { return getDataLink(set, a, original); } } } } catch (Throwable ne) { return null; } return null; } private static final Pattern LINK_ATTR = Pattern.compile("nxfile\\:\\/\\/(.+)\\#(entry.+)"); private static final HObject getDataLink(HObject set, Attribute a, HierarchicalDataFile original) throws Exception { final String[] vals = (String[])a.getValue(); final Matcher matcher = LINK_ATTR.matcher(vals[0]); if (matcher.matches()) { final String path = matcher.group(1); final String fullPath = matcher.group(2); File file = new File(path); if (!file.exists()) { // Look in same directory. final File parent = set.getFileFormat().getAbsoluteFile().getParentFile(); file = new File(parent, file.getName()); } if (!file.exists()) return null; @SuppressWarnings("resource") IHierarchicalDataFile hFile = original!=null ? original.getLinkedFile(file.getAbsolutePath()) // Causes a memory leak I think: : HierarchicalDataFactory.getReader(file.getAbsolutePath()); HObject link = (HObject)hFile.getData(fullPath); link.getMetadata(); // Ensure that meta data is read. return link; } return null; } public static long[] getDims(final IHierarchicalDataFile file, String setPath) throws Exception { HObject obj = (HObject)file.getData(setPath); if (!(obj instanceof Dataset)) return null; Dataset set = (Dataset)obj; if (set.getDims()==null) { set.getMetadata(); } return set.getDims(); } public static long[] getDims(final Dataset set) throws Exception { if (set.getDims()==null) { set.getMetadata(); } return set.getDims(); } public static long getSize(final Dataset set) throws Exception { long[] shape; try { shape = HierarchicalDataUtils.getDims((Dataset)set); } catch (Exception e) { return -1; } if (shape==null) return -1; long size = shape[0]; for (int i = 1; i < shape.length; i++) size*=shape[i]; final int bpi = set.getDatatype().getDatatypeSize(); return bpi*size; } /** * Extract the value of an attribute. * @param value * @return */ public static String extractValue(Object value) { if (value == null) { return ""; } else if (value.getClass().isArray()) { return toString(value); } else { return value.toString(); } } /** * Deals with primitive arrays * @param value */ private static String toString(Object value) { if (value==null) return null; if (value instanceof short[]) { return Arrays.toString((short[])value); } else if (value instanceof int[]) { return Arrays.toString((int[])value); } else if (value instanceof long[]) { return Arrays.toString((long[])value); } else if (value instanceof char[]) { return Arrays.toString((char[])value); } else if (value instanceof float[]) { return Arrays.toString((float[])value); } else if (value instanceof double[]) { return Arrays.toString((double[])value); } else if (value instanceof boolean[]) { return Arrays.toString((boolean[])value); } else if (value instanceof byte[]) { return Arrays.toString((byte[])value); } else if (value instanceof Object[]) { return Arrays.toString((Object[])value); } return value.toString(); } /** * Convert the size == 1 array to a scalar (extract value[0]). Works on * primitive type arrays, String[] and Number[] * * @param value * a {@link Dataset} value (i.e. an array, the result of calling * {@link Dataset#read()}) * @return the scalar value, or <code>null</code> if no scalar can be * extracted */ public static Object extractScalar(Object value) { if (value == null) return null; if (value instanceof short[]) { short[] valueArray = (short[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof int[]) { int[] valueArray = (int[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof long[]) { long[] valueArray = (long[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof char[]) { char[] valueArray = (char[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof float[]) { float[] valueArray = (float[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof double[]) { double[] valueArray = (double[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof boolean[]) { boolean[] valueArray = (boolean[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof byte[]) { byte[] valueArray = (byte[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof String[]) { String[] valueArray = (String[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } else if (value instanceof Number[]) { Number[] valueArray = (Number[]) value; if (valueArray.length == 1) return valueArray[0]; return null; } return null; } /** * Compares the two scalar objects if they are the same type and Comparable * using their compareTo method, else compares the toString value. * * @see Comparable#compareTo(Object) */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static int compareScalars(Object a, Object b) { if (a instanceof Comparable && b instanceof Comparable) { Comparable ca = (Comparable) a; Comparable cb = (Comparable) b; if (a.getClass() == b.getClass()) return ca.compareTo(cb); } return a.toString().compareTo(b.toString()); } /** * Compare object a as a scalar to String b by first converting b to a type * that matches a. * * @see Comparable#compareTo(Object) * @throws NumberFormatException * if b can not be converted to the same type as a */ public static int compareScalarToString(Object a, String b) throws NumberFormatException { if (a instanceof Short) { return ((Short) a).compareTo(Short.parseShort(b)); } if (a instanceof Integer) { return ((Integer) a).compareTo(Integer.parseInt(b)); } if (a instanceof Long) { return ((Long) a).compareTo(Long.parseLong(b)); } if (a instanceof Character) { return ((Character) a).toString().compareTo(b); } if (a instanceof Float) { return ((Float) a).compareTo(Float.parseFloat(b)); } if (a instanceof Double) { return ((Double) a).compareTo(Double.parseDouble(b)); } if (a instanceof Boolean) { return ((Boolean) a).compareTo(Boolean.parseBoolean(b)); } if (a instanceof Byte) { return ((Byte) a).compareTo(Byte.valueOf(b)); } if (a instanceof String) { return ((String) a).compareTo(b); } String name; if (a == null) { name = "null"; } else { name = "a.getClass().getName()"; } throw new NumberFormatException("a has unknown type for conversion: " + name); } }