/* * 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.dataio.provider.cmdline; import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerCmdline; import de.unihalle.informatik.Alida.dataio.provider.ALDDataIOCmdline; import de.unihalle.informatik.Alida.exceptions.ALDDataIOManagerException; import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException; import de.unihalle.informatik.Alida.exceptions.ALDOperatorException; import de.unihalle.informatik.Alida.exceptions.ALDProcessingDAGException; import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException.ALDDataIOProviderExceptionType; import de.unihalle.informatik.Alida.helpers.ALDClassInfo; import de.unihalle.informatik.Alida.helpers.ALDParser; import de.unihalle.informatik.Alida.operator.ALDData; import de.unihalle.informatik.Alida.operator.ALDOperator; import java.lang.reflect.Field; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import java.util.LinkedList; /** * Abstract class providing basic methods for cmdline DataIO providers * according to Alida conventions. *<p> * These conventions are detailed in the documentation if the methods <code>readData</code> and <code>writeData</code> in this class below. * They handle IO from/to file and reading derived classes of the class handled by an dataIO provider. *<p> * Classes extending this class are expected to override the methods <code>parse</code> and * <code>formatAsString</code> which do the actual reading/parsing or writing/formating * subsequent to generic handling of Alida convention with respect to derived classes * and IO form/to file. * * @author posch * */ public abstract class ALDStandardizedDataIOCmdline implements ALDDataIOCmdline { /** As a convention a parameter value starting with this character indicates * that the actual parameter should be read from or writen to a file. * The filename is the remaining string after removing this character. */ public static final char FILEIO_CHAR = '@'; /** As a convention this character starts a derived class name, * and is terminated by ':' */ public static final char DERIVEDCLASS_CHAR = '$'; /** * debugging output */ private boolean debug = false; /** Returns an object instantiated from valueString. * For the class of the object to be read see {@link ALDDataIOManagerCmdline#readData(Field,Class,String)}. * This method is assumed to directly parse the <code>valueString</code> and make no * prior interpretation regarding a file to use or derived class to return. * * @param field Field of object to be returned * @param cl Class of object to be returned. * @param valueString Source from where to read data (e.g. a filename). * @return Object read from valueString * @throws ALDDataIOProviderException * @throws ALDDataIOManagerException * * @see de.unihalle.informatik.Alida.dataio.ALDDataIOManagerCmdline */ abstract public Object parse( Field field, Class<?> cl, String valueString) throws ALDDataIOProviderException, ALDDataIOManagerException; /** Returns the string representations of this object * This method is assumed to directly format the <code>obj</code> into the string return and make no * prior interpretation regarding a file to be used. * * @param obj object to format * @return string representations of the object * @throws ALDDataIOManagerException * @throws ALDDataIOProviderException */ abstract public String formatAsString( Object obj) throws ALDDataIOManagerException, ALDDataIOProviderException; /** Returns the external representations of this object using a formatString. * The <code>formatString</code> may be used define parts of the object to be formated * or specify format charateristics. * However, <code>formatString</code> should not interpreted in order to decide to * output the object directly to a file (as this is handled by <code>writeData</code>). *<p> * The default implementation is equivalent to <code>formatAsString(obj)</code>. * @param obj object to format * @throws ALDDataIOManagerException * @throws ALDDataIOProviderException */ public String formatAsString( Object obj, String formatString) throws ALDDataIOManagerException, ALDDataIOProviderException { return formatAsString( obj); } /** Read an object using <code>valueString</code>. * For the class of the object to be read see {@link ALDDataIOCmdline}. * <p> * If <code>valueString</code> starts with <code>FILEIO_CHAR</code> the value is read from a file * where the name of this file is the remaining string of <code>valueString</code>. * subsequent to <code>FILEIO_CHAR</code>. * <p> * The next step in interpreting the value string is scrutinze * whether an instance of a class deriving the class defined in <code>field</code> or * the class <code>cl</code> is to be returned. * This is indicated by starting the value string (either passed directly via <code>valueString</code> * or the string read from file) with <code>DERIVEDCLASS_CHAR</code>. * by <code>field</code> * If this is the case all charaters up, but excluding, the next colon are * interpreted as this fully qualified class name. * Next it is checked if this class indeed is a proper extension * which in turn required this class to be annotated with {@link de.unihalle.informatik.Alida.annotations.ALDDerivedClass}. * If this check passes * a dataIO provider of this class is looked up and its <code>readData</code> * invoked with the value string after the deliminating colon. * <p> * If no deriving class is requested, the parse method of the * class defined in <code>field</code> or * the class <code>cl</code> is invoked on the value string. * * @param field Field of object to be returned. * @param cl Class of object to be returned. * @param valueString Source from where to read data (e.g. a filename). * @return Object read from source. * @throws ALDDataIOProviderException * @throws ALDDataIOManagerException * */ /* (non-Javadoc) * @see de.unihalle.informatik.Alida.helpers.ALDDataIO#writeData(java.lang.Object, java.lang.String) */ @Override public Object readData(Field field, Class<?> cl, String valueString) throws ALDDataIOProviderException, ALDDataIOManagerException { if ( debug ) { System.out.println( "ALDStandardizedDataIOCmdline::readData for class <" + cl.getCanonicalName() + "> with valueString <" + valueString + ">"); } if ( field != null ) cl = field.getType(); // potentially read the value string from file and remember filename String filename = null; valueString = valueString.trim(); if ( valueString.length() > 0 && valueString.charAt(0) == FILEIO_CHAR ) { filename = valueString.substring( 1); valueString = getValueStringFromFile( filename); } Object obj; valueString = valueString.trim(); if ( valueString.length() == 0 || valueString.charAt(0) != DERIVEDCLASS_CHAR ) { // not a derived class, parse the string obj = parse( field, cl, valueString); } else { // handle derived classes int indexOfSep = valueString.indexOf( ':', 1); if ( indexOfSep == -1 ) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.SYNTAX_ERROR, "ALDStandardizedDataIOCmdline::readData derived class without <:> in " + valueString); } String className = valueString.substring( 1, indexOfSep); valueString = valueString.substring( indexOfSep+1); LinkedList<Class> derivedClassesFound = new LinkedList<Class>(); Collection<Class> derivedClasses = ALDClassInfo.lookupExtendingClasses( cl); for ( Class derivedClass : derivedClasses ) { if ( className.equals( derivedClass.getName()) ) { derivedClassesFound.clear(); derivedClassesFound.add( derivedClass); break; } else if ( derivedClass.getName().endsWith( className) ) { derivedClassesFound.add( derivedClass); } } if ( derivedClassesFound.size() != 1 ) { StringBuffer msg = new StringBuffer("ALDStandardizedDataIOCmdline::readData found " + derivedClassesFound.size() + " derived classes matching <" + className + ">"); msg.append( " derived classes available:"); for ( Class derivedClass : derivedClasses ) { msg.append( "\t" + derivedClass.getName()); } throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.OBJECT_TYPE_ERROR, new String( msg)); } ALDDataIOCmdline provider = null; if ( debug ) { System.out.println("ALDStandardizedDataIOCmdline::readData trying to find a provider for class <" + derivedClassesFound.peek() + ">"); } provider = (ALDDataIOCmdline)(ALDDataIOManagerCmdline.getInstance().getProvider( derivedClassesFound.peek(), ALDDataIOCmdline.class)); obj = provider.readData( null, derivedClassesFound.peek(), valueString); } // optionally read history if ( filename != null && ALDDataIOManagerCmdline.getInstance().isDoHistory()) { ALDOperator.readHistory(obj, filename); if ( obj instanceof ALDData ) { ((ALDData)obj).setLocation(filename); } } return obj; } /** * Return a string representation of given object value or print it to a file. * <p> * If the given <code>formatString</code> starts with <code>FILEIO_CHAR</code> standard out is used as * target location, otherwise the string remaining subsequent to <code>FILEIO_CHAR</code> is interpreted as file name. * In turn, if the remaining string starts with <code>+</code> the output is apended to the file (with <code>+</code> * removed as filename). * @throws ALDDataIOManagerException * @throws ALDDataIOProviderException */ /* (non-Javadoc) * @see de.unihalle.informatik.Alida.helpers.ALDDataIO#writeData(java.lang.Object, java.lang.String) */ @Override public String writeData(Object obj, String formatString) throws ALDDataIOManagerException, ALDDataIOProviderException { // writer object BufferedWriter bufWriter = null; if ( formatString.length() > 0 && formatString.charAt( 0) == FILEIO_CHAR ) { boolean append = false; String filename = null; try { if ( formatString.length() > 1 && formatString.charAt( 1) == '+' ) { append = true; filename = formatString.substring(2); } else { filename = formatString.substring(1); } bufWriter = new BufferedWriter(new FileWriter( filename, append)); } catch (IOException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.FILE_IO_ERROR, "ALDStandardizedDataIOCmdline::writeData cannot open writer for filename <" + filename + ">" + (append ? " in append mode" : "")); /* should we try to write to stdout ?? bufWriter = new BufferedWriter(new PrintWriter(System.out)); try { bufWriter.write(this.formatAsString( obj) + "\n"); bufWriter.flush(); bufWriter.close(); } catch (IOException e2) { System.err.println("ALDEnumDataIOCmdline: printing to standard out failed"); return null; } */ } try { bufWriter.write(this.formatAsString( obj) + "\n"); bufWriter.flush(); bufWriter.close(); } catch (IOException e1) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.FILE_IO_ERROR, "ALDStandardizedDataIOCmdline::writeData error writing to file <" + filename + ">"); } if ( ALDDataIOManagerCmdline.getInstance().isDoHistory()) { try { ALDOperator.writeHistory(obj, filename); } catch (ALDProcessingDAGException e) { // ignore this error } catch (ALDOperatorException e) { // ignore this error } } return null; } else { if ( formatString.length() > 0 && ALDParser.brackets.get( formatString.charAt(0)) != null) { return this.formatAsString( obj, formatString); } else { return this.formatAsString( obj); } } } /** The complete content of the file is return as one string. * * @param formatString * @return argument or string read from file * @throws ALDDataIOProviderException */ private static String getValueStringFromFile( String filename) throws ALDDataIOProviderException { try { BufferedReader reader = new BufferedReader( new FileReader(filename)); StringBuffer buf = new StringBuffer(); String line; while ( (line = reader.readLine()) != null ) { buf.append( line); } return( new String( buf)); } catch (FileNotFoundException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.FILE_IO_ERROR, "ALDStandardizedDataIOCmdline::getValueStringFromFile cannot open reader for file <" + filename + ">"); } catch (IOException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.FILE_IO_ERROR, "ALDStandardizedDataIOCmdline::getValueStringFromFile cannot read line from <" + filename + ">"); } } }