/* * This file is part of Alida, a Java library for * Advanced Library for Integrated Development of Data Analysis Applications. * * Copyright (C) 2010 - @YEAR@ * * 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 3 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/>. * * Fore more information on Alida, visit * * http://www.informatik.uni-halle.de/alida/ * */ /* * Most recent change(s): * * $Rev$ * $Date$ * $Author$ * */ package de.unihalle.informatik.Alida.helpers; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.*; import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException; import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException.ALDDataIOProviderExceptionType; /** * Helper class for parsing/formatting objects for data I/O in Alida. * <p> * This class supplies convenience methods to parse and format objects * according to Alida's DataIO conventions. * * @author posch */ public class ALDParser { private static boolean debug = false; /** * Hashmap to hold pairs of opening and closing brackets. */ public static HashMap<Character,Character> brackets = new HashMap<Character,Character>(); static { brackets.put( '(', ')'); brackets.put( '{', '}'); brackets.put( '[', ']'); } /** * Parses a string for a matching bracket. * <p> * The first character of this string is interpreted as opening backet, * the closing bracket is assumed to coincide with the internal * definition in <code>brackets</code>. Upon return the outmost matching * pair of brackets is removed from <code>str</code>. * * @param str String to parse * @return String enclosed by outmost pair of matching brackets. */ public static String parseBracket( String str) { if ( str.length() < 1 ) return null; Character closeBracket = brackets.get( str.charAt(0)); if ( closeBracket != null ) return parseBracket( str, closeBracket); else return null; } /** * Parses a string for a matching bracket. * <p> * The first character of this string is interpreted as opening backet, * the character <code>closeBracket</code> as closing bracket. * * @param str String to parse. * @param closeBracket Closing bracket character. * @return String enclosed by most outer pair of matching brackets. */ public static String parseBracket( String str, char closeBracket) { if ( str.length() < 1 ) return null; char openBracket = str.charAt(0); int count = 1; // open bracket int idx = 1; while ( count > 0 && idx < str.length() ) { if ( str.charAt( idx) == closeBracket ) count--; else if ( str.charAt( idx) == openBracket ) count++; idx++; } if ( count == 0 ) return str.substring( 1, idx-1); else return null; } /** * Parses a comma separated list of 'name=value' pairs into a hash map. * <p> * The names found are used as keys in the hash map, while the values * are put into the map as corresponding values. * * @param str String to parse. * @return Hash map with name and value pairs. * @throws ALDDataIOProviderException */ public static HashMap<String,String> parseNameValuePairs( String str) throws ALDDataIOProviderException { if ( debug ) { System.out.println( "ALDParser::parseNameValuePairs using <" + str + ">"); } HashMap<String,String> map = new HashMap<String,String>(); for ( String pair : ALDParser.split( str, ',') ) { ArrayList<String> parts = new ArrayList<String>(ALDParser.split( pair.trim(), '=')); if ( parts.size() == 1 ) { map.put( parts.get(0).trim(), ""); } else if ( parts.size() != 2 ) { throw new ALDDataIOProviderException(ALDDataIOProviderExceptionType.SYNTAX_ERROR, "ALDParser::parseNameValuePairs found = sign " + (parts.size() -1) + " times, instead of once"); } else { map.put( parts.get(0).trim(), parts.get(1).trim()); } } return map; } /** * Split a string at each occurance of <code>sepChar</code>. * <p> * Note that occurances of <code>sepChar</code> enclosed in * brackets are not considered as separators. * * @param str String to split. * @param sepChar Separating character. * @return Linked list containing the separated parts of <code>str</code>. */ public static LinkedList<String> split( String str, char sepChar) { LinkedList<String> parts = new LinkedList<String>(); int startIdx = 0; int endIdx = 0; while ( endIdx < str.length() ) { //System.out.println( "split, endIdx = " + endIdx); if ( str.charAt( endIdx) == sepChar ) { parts.add( str.substring( startIdx, endIdx)); endIdx++; startIdx = endIdx; } else if ( brackets.get( str.charAt( endIdx)) != null ) { char closeBracket = brackets.get( str.charAt( endIdx)); String aux = parseBracket( str.substring( endIdx), closeBracket); //System.out.println( "split, found bracket: " + str.charAt( endIdx) + " - " + closeBracket + " aux = " + aux) ; if ( aux != null ) { endIdx += aux.length() + 2; } else { endIdx = str.length(); //parts.add( str.substring( startIdx, endIdx)); } } else { endIdx++; } } if ( startIdx != endIdx ) { parts.add( str.substring( startIdx, endIdx)); } return parts; } /** * Formats an array according to Alida data I/O conventions. * * @param obj Array to be formatted. * @return Object formatted as string **/ public static String arrayToString( Object obj) { StringBuffer buf; if (obj.getClass().getName().startsWith("[")) { buf = new StringBuffer(); Object [] array; // native datatypes if ( obj.getClass().equals(boolean[].class )) { boolean[] pArray = (boolean[])obj; array = new Boolean[pArray.length]; for ( int i=0 ; i < pArray.length ; i++ ) array[i] = pArray[i]; } else if ( obj.getClass().equals(byte[].class )) { byte[] pArray = (byte[])obj; array = new Byte[pArray.length]; for ( int i=0 ; i < pArray.length ; i++ ) array[i] = pArray[i]; } else if ( obj.getClass().equals(double[].class )) { double[] pArray = (double[])obj; array = new Double[pArray.length]; for ( int i=0 ; i < pArray.length ; i++ ) array[i] = pArray[i]; } else if ( obj.getClass().equals(float[].class )) { float[] pArray = (float[])obj; array = new Float[pArray.length]; for ( int i=0 ; i < pArray.length ; i++ ) array[i] = pArray[i]; } else if ( obj.getClass().equals(int[].class )) { int[] pArray = (int[])obj; array = new Integer[pArray.length]; for ( int i=0 ; i < pArray.length ; i++ ) array[i] = pArray[i]; } else if ( obj.getClass().equals(short[].class )) { short[] pArray = (short[])obj; array = new Short[pArray.length]; for ( int i=0 ; i < pArray.length ; i++ ) array[i] = pArray[i]; } else { array = (Object[])obj; } buf.append("["); int index = 0; for (Object o: array) { if (index == 0) buf.append(o.toString()); else buf.append("," + o.toString()); ++index; } buf.append("]"); return new String( buf); } else { return null; } } /** * Parses a string into a 1D-array. * <p> * The type of the returned array depends on the specified class. * Only primitive and data wrapper types as well as strings are support at the moment. * The <code>valueString</code> is assume to contain a comma separated list * of nested <code>valueString</code> * for each element of the array enclosed in matching square brackets, * e.g. <code>[1.2 , 2.2 , 3.1]</code>. * * @param cl Desired type of array. * @param valueString String to parse. * @return Array of specified type filled with elements from string. * @throws ALDDataIOProviderException */ public static Object readArray1D(Class<?> cl, String valueString) throws ALDDataIOProviderException { String [] elements = null; String arrStr = valueString.trim(); // if string contains a at least one "," we parse the string directly if (arrStr.contains("[")) { if (arrStr.startsWith("[")) { arrStr = arrStr.substring(1); } if (arrStr.endsWith("]")) { arrStr = arrStr.substring(0, arrStr.length()-1); } elements = arrStr.split(","); } // otherwise we interpret the string as file and try to parse the file else { BufferedReader bufRead = null; try { bufRead = new BufferedReader(new FileReader(valueString)); } catch (FileNotFoundException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.FILE_IO_ERROR, "ALDParser::readArray1D cannot open array file " + "\"" + valueString + "\", exiting..."); } try { arrStr = bufRead.readLine(); if (arrStr.startsWith("[")) { arrStr = arrStr.substring(1); } if (arrStr.endsWith("]")) { arrStr = arrStr.substring(0, arrStr.length()-1); } elements = arrStr.split(","); } catch (IOException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.FILE_IO_ERROR, "ALDParser::readArray1D cannot read array from file " + "\"" + valueString + "\", exiting..."); } } // split string into elements and do security check if (elements == null) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.SYNTAX_ERROR, "ALDParser::readArray1D cannot find 1D arrays in <" + arrStr + ">"); } int index = 0; // wrapper datatypes try { if (cl.equals(Boolean[].class )) { Boolean [] array = new Boolean[elements.length]; index = 0; for (String e: elements) { array[index] = Boolean.valueOf(e); ++index; } return array; } else if (cl.equals(Byte[].class )) { Byte [] array = new Byte[elements.length]; index = 0; for (String e: elements) { array[index] = Byte.valueOf(e); ++index; } return array; } else if (cl.equals(Double[].class )) { Double [] array = new Double[elements.length]; index = 0; for (String e: elements) { array[index] = Double.valueOf(e); ++index; } return array; } else if (cl.equals(Float[].class )) { Float [] array = new Float[elements.length]; index = 0; for (String e: elements) { array[index] = Float.valueOf(e); ++index; } return array; } else if (cl.equals(Integer[].class )) { Integer [] array = new Integer[elements.length]; index = 0; for (String e: elements) { array[index] = Integer.valueOf(e); ++index; } return array; } else if (cl.equals(Short[].class )) { Short [] array = new Short[elements.length]; index = 0; for (String e: elements) { array[index] = Short.valueOf(e); ++index; } return array; } else if (cl.equals(String[].class )) { return elements; // native datatypes } else if (cl.equals(boolean[].class )) { boolean [] array = new boolean[elements.length]; index = 0; for (String e: elements) { array[index] = Boolean.valueOf(e).booleanValue(); ++index; } return array; } else if (cl.equals(byte[].class )) { byte [] array = new byte[elements.length]; index = 0; for (String e: elements) { array[index] = Byte.valueOf(e).byteValue(); ++index; } return array; } else if (cl.equals(double[].class )) { double [] array = new double[elements.length]; index = 0; for (String e: elements) { array[index] = Double.valueOf(e).doubleValue(); ++index; } return array; } else if (cl.equals(float[].class )) { float [] array = new float[elements.length]; index = 0; for (String e: elements) { array[index] = Float.valueOf(e).floatValue(); ++index; } return array; } else if (cl.equals(int[].class )) { int [] array = new int[elements.length]; index = 0; for (String e: elements) { array[index] = Integer.valueOf(e).intValue(); ++index; } return array; } else if (cl.equals(short[].class )) { short [] array = new short[elements.length]; index = 0; for (String e: elements) { array[index] = Short.valueOf(e).shortValue(); ++index; } return array; } } catch (Exception e) { throw new ALDDataIOProviderException(ALDDataIOProviderExceptionType.SYNTAX_ERROR, "ALDParser::readArray1D cannot read " + index + "-th element " + " from <" + elements[index] + ">\n" + " " + e.toString()); } throw new ALDDataIOProviderException(ALDDataIOProviderExceptionType.OBJECT_TYPE_ERROR, "ALDParser::readArray1D unknown element type <" + cl.getCanonicalName() + ">"); } /** * Parses a string into a 2D-array. * <p> * The type of the returned array depends on the specified class. * Only primitive and data wrapper types as well as strings are support at the moment. * The <code>valueString</code> is assume to contain a comma separated list * of nested <code>valueString</code> * for each element of the array enclosed in matching square brackets, * These values string are as specified fo 1D-arrays, i.e. again * lists of comma separated values enclosed in matching square brackets. * e.g. <code>[ [1 , 2] , [3, 4]]</code>. * * @param cl Desired type of array. * @param valueString String to parse. * @return Array of specified type filled with elements from string. */ /** * Parses a string into a 2D-array. * * @param cl Desired type of array. * @param valueString Input string to parse. * @return Array of specified type filled with elements from string. * @throws ALDDataIOProviderException */ public static Object readArray2D(Class<?> cl, String valueString) throws ALDDataIOProviderException { if ( valueString == null) throw new ALDDataIOProviderException(ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParser::readArray2D valueString must not be null"); String elementString = null; String arrStr = valueString.trim(); // if string contains a at least one "[" we parse the string directly if (arrStr.contains("[")) { if (arrStr.startsWith("[")) { arrStr = arrStr.substring(1); } if (arrStr.endsWith("]")) { arrStr = arrStr.substring(0, arrStr.length()-1); } elementString = arrStr; } // otherwise we interpret the string as file and try to parse the file // TODO: probably this is not necessary any more as standardized provider is used else { BufferedReader bufRead = null; try { bufRead = new BufferedReader(new FileReader(valueString)); } catch (FileNotFoundException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.FILE_IO_ERROR, "ALDParser::readArray2D cannot open array file " + "\"" + valueString + "\", exiting..."); } try { arrStr = bufRead.readLine(); if (arrStr.startsWith("[")) { arrStr = arrStr.substring(1); } if (arrStr.endsWith("]")) { arrStr = arrStr.substring(0, arrStr.length()-1); } elementString = arrStr; } catch (IOException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.FILE_IO_ERROR, "ALDParser::readArray2D cannot read array from file " + "\"" + valueString + "\", exiting..."); } } LinkedList<String> sublists = ALDParser.split(elementString,','); if (sublists.isEmpty()) throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.SYNTAX_ERROR, "ALDParser::readArray2D cannot find 1D arrays in <" + elementString + ">"); // split each sublist into its components LinkedList<LinkedList<String>> sublistsSplitted = new LinkedList<LinkedList<String>>(); for (String slist: sublists) { slist = slist.trim(); if (slist.startsWith("[")) { slist = slist.substring(1); } if (slist.endsWith("]")) { slist = slist.substring(0, slist.length()-1); } LinkedList<String> elements = ALDParser.split(slist,','); sublistsSplitted.add(elements); } int ydim = sublists.size(); int xdim = sublistsSplitted.get(0).size(); int x = 0; int y = 0; // wrapper datatypes try { if (cl.equals(Boolean[][].class )) { Boolean [][] array = new Boolean[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Boolean.valueOf(sublistsSplitted.get(y).get(x)); } } return array; } else if (cl.equals(Byte[][].class )) { Byte [][] array = new Byte[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Byte.valueOf(sublistsSplitted.get(y).get(x)); } } return array; } else if (cl.equals(Double[][].class )) { Double [][] array = new Double[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Double.valueOf(sublistsSplitted.get(y).get(x)); } } return array; } else if (cl.equals(Float[][].class )) { Float [][] array = new Float[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Float.valueOf(sublistsSplitted.get(y).get(x)); } } return array; } else if (cl.equals(Integer[][].class )) { Integer [][] array = new Integer[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Integer.valueOf(sublistsSplitted.get(y).get(x)); } } return array; } else if (cl.equals(Short[][].class )) { Short [][] array = new Short[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Short.valueOf(sublistsSplitted.get(y).get(x)); } } return array; } else if (cl.equals(String[][].class )) { String [][] array = new String[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = sublistsSplitted.get(y).get(x); } } // native datatypes } else if (cl.equals(boolean[][].class )) { boolean [][] array = new boolean[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Boolean.valueOf(sublistsSplitted.get(y).get(x)).booleanValue(); } } return array; } else if (cl.equals(byte[][].class )) { byte [][] array = new byte[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Byte.valueOf(sublistsSplitted.get(y).get(x)).byteValue(); } } return array; } else if (cl.equals(double[][].class )) { double [][] array = new double[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Double.valueOf(sublistsSplitted.get(y).get(x)).doubleValue(); } } return array; } else if (cl.equals(float[][].class )) { float [][] array = new float[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Float.valueOf(sublistsSplitted.get(y).get(x)).floatValue(); } } return array; } else if (cl.equals(int[][].class )) { int [][] array = new int[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Integer.valueOf(sublistsSplitted.get(y).get(x)).intValue(); } } return array; } else if (cl.equals(short[][].class )) { short [][] array = new short[ydim][xdim]; for (y=0; y<ydim; ++y) { for (x=0; x<xdim; ++x) { array[y][x] = Short.valueOf(sublistsSplitted.get(y).get(x)).shortValue(); } } return array; } } catch (Exception e) { throw new ALDDataIOProviderException(ALDDataIOProviderExceptionType.SYNTAX_ERROR, "ALDParser::readArray2D cannot read " + x + "-th element in " + y + "-th row" + " from <" + sublistsSplitted.get(y).get(x) + ">\n" + " " + e.toString()); } throw new ALDDataIOProviderException(ALDDataIOProviderExceptionType.OBJECT_TYPE_ERROR, "ALDParser::readArray2D unknown element type <" + cl.getCanonicalName() + ">"); } }