/*
* 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.dataio.provider.helpers.ALDParametrizedClassDataIOHelper;
import de.unihalle.informatik.Alida.dataio.provider.helpers.ALDParametrizedClassDummy;
import de.unihalle.informatik.Alida.exceptions.ALDDataIOManagerException;
import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException;
import de.unihalle.informatik.Alida.exceptions.ALDDataIOProviderException.ALDDataIOProviderExceptionType;
import de.unihalle.informatik.Alida.exceptions.ALDOperatorException;
import de.unihalle.informatik.Alida.helpers.ALDParser;
import de.unihalle.informatik.Alida.operator.ALDOpParameterDescriptor;
import de.unihalle.informatik.Alida.operator.ALDOperator;
import de.unihalle.informatik.Alida.annotations.ALDDataIOProvider;
import de.unihalle.informatik.Alida.annotations.Parameter.Direction;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.LinkedList;
import java.util.HashMap;
/**
* DataIO provider for parametrized classes and operators from command line.
* As this provider extends {@link ALDStandardizedDataIOCmdline} it
* implements the Alida syntax conventions.
* <p>
* For parametrized classes reading is done only for parameters annotated
* with {@link de.unihalle.informatik.Alida.annotations.ALDClassParameter}.
* Either all annotated parameters are written/formated or a
* subset as specified by a format string.
* <p>
* For oeprators reading is done only for IN and INOUT parameters.
* Either all OUT and INPUT parameters are written/formated or a
* subset as specified by a format string.
*
* @author posch
*
*/
@ALDDataIOProvider
public class ALDParametrizedClassDataIOCmdline extends ALDStandardizedDataIOCmdline {
/**
* debug messages
*/
private boolean debug= false;
/**
* Interface method to announce class for which IO is provided for
*
* @return Collection of classes provided
*/
@Override
public Collection<Class<?>> providedClasses() {
LinkedList<Class<?>> classes = new LinkedList<Class<?>>();
classes.add( ALDParametrizedClassDummy.class);
classes.add( ALDOperator.class);
return classes;
}
/** Parser for parametrized classes and ALDOperators.
* Expects a comma separated list of name=value pairs enclosed in curly brackets.
* For the class of the object to be read see {@link ALDDataIOCmdline}.
* <p>
* For parametrized classes each name has to be an annotated parameter.
* <p>
* For operators each name has to be an IN or INOUT parameter name of the operator and receives its value from
* the <code>valueString</code>.
* <p>
* If the list of name=value pairs is empty, i.e. no parameters are to be parsed,
* the empty string is accepted, too.
* @throws ALDDataIOProviderException
* @throws ALDDataIOManagerException
*/
@Override
public Object parse(Field field, Class<?> cl, String valueString)
throws ALDDataIOProviderException, ALDDataIOManagerException {
if ( debug ) {
System.out.println( "ALDParametrizedClassDataIOCmdline::parse using " + valueString);
}
return parse( field, cl, valueString, null);
}
/** Generic Parser for parametrized classes and ALDOperators.
* Expects a comma separated list of name=value pairs enclosed in curly brackets.
* For the class of the object to be read see {@link ALDDataIOCmdline}.
* <p>
* For parametrized classes each name has to be an annotated parameter.
* <p>
* For operators each name has to be an IN or INOUT parameter name of the operator and receives its value from
* the <code>valueString</code>.
* <p>
* If the list of name=value pairs is empty, i.e. no parameters are to be parsed,
* the empty string is accepted, too.
* <p>
* For the class of the object to be read see {@link ALDDataIOCmdline}.
* As a <code>valueString</code> a comma separated list of name=value pairs enclosed in curly brackets
* is expected. Each name has to be a member of the class or a super class which is annotated
* with {@link de.unihalle.informatik.Alida.annotations.ALDClassParameter}.
* The <code>readData</code> method of the provider for the class of the member variable
* is used to read the objects value from <code>value</code>.
*
* @param field
* @param cl
* @param valueString
* @param op if a ALDOperator is to be parse this is an instance of this class. Ignored when parsing
* a parametrized class
* @return
* @throws ALDDataIOProviderException
* @throws ALDDataIOManagerException
*/
public Object parse(Field field, Class<?> cl, String valueString, ALDOperator op)
throws ALDDataIOProviderException, ALDDataIOManagerException {
if ( field != null )
cl = field.getType();
// if op != null we assume that parse was called from oprunner
// and generate slightly different exception comments
boolean calledFromOprunner;
if ( op != null )
calledFromOprunner = true;
else
calledFromOprunner = false;
boolean parseOp;
if ( ALDOperator.class.isAssignableFrom(cl) )
parseOp = true;
else
parseOp = false;
valueString = valueString.trim();
if ( valueString.equals("") ) {
valueString = "{}";
}
String pairStr;
if ( valueString.charAt(0) != '{' ||
(pairStr = ALDParser.parseBracket( valueString, '}')) == null ) {
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.SYNTAX_ERROR,
"ALDParametrizedClassDataIOCmdline::parse cannot find matching {} in " +
valueString);
}
if ( debug ) {
System.out.println("ALDParametrizedClassDataIOCmdline::parse for " +
(parseOp ? "an opertor" : " a parametrized class" +
" with pair string <" + pairStr + ">"));
}
// initialize object to return
Object pClassObj = null;
if ( parseOp ) {
if ( op == null ) {
try {
op = (ALDOperator)(cl.newInstance());
} catch (Exception e ) {
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.OBJECT_INSTANTIATION_ERROR,
"ALDParametrizedClassDataIOCmdline::parse cannot instantiate class <" +
cl.getCanonicalName() + ">");
}
}
} else {
try {
pClassObj = cl.newInstance();
} catch (Exception e ) {
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.OBJECT_INSTANTIATION_ERROR,
"ALDParametrizedClassDataIOCmdline::parse cannot instantiate class <" +
cl.getCanonicalName() + ">");
}
}
// parse the value string
if ( ! pairStr.trim().equals("") ) {
HashMap<String,String> nameValuePairs = ALDParser.parseNameValuePairs( pairStr.trim());
HashMap<String,Field> fieldMap = null;
HashMap<String, Class<?>> classMap = new HashMap<String, Class<?>>(0);
// initialize fieldmap
if ( parseOp) {
fieldMap = new HashMap<String, Field>();
for ( String name : nameValuePairs.keySet() ) {
String paramValueString = nameValuePairs.get( name);
ALDOpParameterDescriptor descr = null;
String pName = null;
try {
LinkedList<String> pNames = lookupParameternames( op, name);
pName = pNames.getFirst();
if ( debug ) {
System.out.println( "Use parameter <" + pName + ">" +
" for <" + name + "> using <" + paramValueString + "> as value");
}
descr = op.getParameterDescriptor( pName);
if ( descr.getDirection() == Direction.IN ||
descr.getDirection() == Direction.INOUT ) {
fieldMap.put( name, descr.getField());
classMap.put(pName, descr.getMyclass());
}
} catch (Exception e) {
// handle this later consistently with parametrized class
}
}
} else {
fieldMap = ALDParametrizedClassDataIOHelper.getAnnotatedFields( cl);
}
// now parse the list of name-value pairs
for ( String name : nameValuePairs.keySet() ) {
if ( fieldMap.containsKey( name) ) {
// field f may be null
Field f = fieldMap.get( name);
// parameter class may be null
Class<?> pClass = classMap.get(name);
Object value;
String elementValueString = nameValuePairs.get( name);
try {
value = ALDDataIOManagerCmdline.getInstance().readData( f, pClass,elementValueString);
} catch (ALDDataIOManagerException e) {
throw new ALDDataIOManagerException( e.getType(),
"ALDParametrizedClassDataIOCmdline::parse cannot read element <" +
name + "> \n of class <" +
(f != null ? f.getType().getCanonicalName() : pClass) +
">\n from <" + elementValueString + ">" +
(!calledFromOprunner ?
"\nwithin <" + valueString + ">\n" : "") +
"\n" + e.getCommentString());
} catch (ALDDataIOProviderException e) {
throw new ALDDataIOProviderException( e.getType(),
"ALDParametrizedClassDataIOCmdline::parse cannot read element <" +
name + ">\n of class <" +
(f != null ? f.getType().getCanonicalName() : pClass) +
">\n from <" + elementValueString + ">" +
(!calledFromOprunner ?
"\nwithin <" + valueString + ">\n" : "") +
"\n" + e.getCommentString());
}
if ( parseOp) {
try {
op.setParameter( name, value);
} catch (ALDOperatorException e) {
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR,
"ALDParametrizedClassDataIOCmdline::parse internal error, cannot set value of member variable <" +
name +">");
}
} else {
try {
ALDParametrizedClassDataIOHelper.setValue( fieldMap.get( name), pClassObj, value);
} catch (IllegalAccessException e) {
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR,
"ALDParametrizedClassDataIOCmdline::parse internal error, cannot set value of member variable <" +
name +">");
}
}
} else { // unknown parameter name
StringBuffer msg = new StringBuffer(" existing parameters:");
for ( String key : fieldMap.keySet() )
msg.append( " " + key);
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.OBJECT_TYPE_ERROR,
"ALDParametrizedClassDataIOCmdline::parse" +
(parseOp ? op : pClassObj).getClass().getName() +
" does not contain a parameter " + name +
new String( msg));
}
}
}
if ( parseOp)
return op;
else
return pClassObj;
}
/**
* Format all parameters of this parametrized class annotated
* with {@link de.unihalle.informatik.Alida.annotations.ALDClassParameter}
* into a string.
* If <code>formatString</code> starts with a curly bracket it is assume
* to contained a comma seprated list of name=value pairs enclosed in a
* matching curly brackets.
* In this case, only the (annotated) members named in this list are formated where the <code>value</code>
* is passed to the <code>writeData</code> of the dataIO provider handling the parameter's type.
* In extension, is a name equals <code>*</code> all members non listed in the
* <code>formatString</code> are formated using the <code>value</code> of this pair.
*<p>
* If <code>formatString</code> does not start with a curly bracket all annotated
* members are formated.
*
* @param obj parametrized class to be formated
* @param formatString
* @throws ALDDataIOProviderException
* @throws ALDDataIOManagerException
*/
public String formatAsString(Object obj, String formatString)
throws ALDDataIOProviderException, ALDDataIOManagerException {
if ( formatString == null ) {
return this.formatAsString( obj);
} else {
// if we do not find an opening bracket, format without format string
if ( formatString.trim().charAt(0) != '{' ) {
return formatAsString( obj);
}
String pairStr = null;
// if we cannot parse brackets, format without format string
if ( (pairStr = ALDParser.parseBracket( formatString)) == null ) {
return formatAsString( obj);
}
HashMap<String,String> nameValuePairs = ALDParser.parseNameValuePairs( pairStr);
HashMap<String,Field> fieldMap;
if ( obj instanceof ALDOperator) {
fieldMap = new HashMap<String, Field>();
for ( String pName : ((ALDOperator)obj).getOutInoutNames() ) {
try {
fieldMap.put( pName, ((ALDOperator)obj).getParameterDescriptor(pName).getField());
} catch (ALDOperatorException e) {
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR,
"ALDParametrizedClassDataIOCmdline::formatAsString internal error: can not get descriptor for <" +
pName + ">");
}
}
} else {
fieldMap = ALDParametrizedClassDataIOHelper.getAnnotatedFields( obj.getClass());
}
StringBuffer bufstr = new StringBuffer( "{ ");
boolean foundAsterix = false;
String generalFormatString = null;
for ( String name : nameValuePairs.keySet() ) {
if ( name.equals( "*") ) {
foundAsterix = true;
generalFormatString = nameValuePairs.get( name);
continue;
}
if ( fieldMap.containsKey( name) ) {
this.addParameter( name, obj, fieldMap.get( name), nameValuePairs.get( name), bufstr);
} else {
StringBuffer msg = new StringBuffer("\n existing members:\n");
for ( String key : fieldMap.keySet() )
msg.append( " " + key + "\n");
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.OBJECT_TYPE_ERROR,
"ALDParametrizedClassDataIOCmdline::formatAsString class <" +
obj.getClass().getName() +
">\n does not contain an annotated member <" + name + ">"+
new String( msg));
}
}
if ( foundAsterix ) {
for ( String name : fieldMap.keySet() ) {
if ( ! nameValuePairs.containsKey( name) ) {
this.addParameter( name, obj, fieldMap.get( name), generalFormatString, bufstr);
}
}
}
if ( bufstr.length() > 3)
return new String( bufstr.delete( bufstr.length()-3, bufstr.length()).append(" }"));
else
return new String( bufstr.append(" }"));
}
}
/**
* Generic formatter to string of parametrized classes.
* Output all annotated members of the class annotated
* with {@link de.unihalle.informatik.Alida.annotations.ALDClassParameter}.
* @throws ALDDataIOProviderException
* @throws ALDDataIOManagerException
*/
@Override
public String formatAsString(Object obj)
throws ALDDataIOProviderException, ALDDataIOManagerException {
StringBuffer bufstr = new StringBuffer( "{ ");
HashMap<String,Field> fieldMap;
if ( obj instanceof ALDOperator) {
fieldMap = new HashMap<String, Field>();
for ( String pName : ((ALDOperator)obj).getOutInoutNames() ) {
try {
fieldMap.put( pName, ((ALDOperator)obj).getParameterDescriptor(pName).getField());
} catch (ALDOperatorException e) {
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR,
"ALDParametrizedClassDataIOCmdline::formatAsString internal error: can not get descriptor for <" +
pName + ">");
}
}
} else {
fieldMap = ALDParametrizedClassDataIOHelper.getAnnotatedFields( obj.getClass());
}
for ( String name : fieldMap.keySet() ) {
this.addParameter( name, obj, fieldMap.get( name), "-", bufstr);
}
if ( bufstr.length() > 3)
return new String( bufstr.delete( bufstr.length()-3, bufstr.length()).append(" }"));
else
return new String( bufstr.append(" }"));
}
/** Format the parameter <code>name</code> of the object <code>obj</code> into the buffer <code>bufstr</code>
* using <code>formatString</code> to determine formating.
*
* @param name parameter to be formated
* @param obj object for which to format parameter
* @param field field of parameter to be formated
* @param bufstr String buffer to append formated parameter
* @throws ALDDataIOProviderException
* @throws ALDDataIOManagerException
*/
private void addParameter( String name, Object obj, Field field, String formatString, StringBuffer bufstr)
throws ALDDataIOProviderException, ALDDataIOManagerException {
Object value = null;
try {
value = ALDParametrizedClassDataIOHelper.getValue( field, obj);
} catch (Exception ex) {
throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR,
"ALDParametrizedClassDataIOCmdline::addParameter internal error, cannot get value of member variable <" +
name +">");
}
String str;
str= ALDDataIOManagerCmdline.getInstance().writeData(value,formatString);
if ( str != null ) {
bufstr.append( name + "=" + str + " , ");
} else {
bufstr.append( name + " written using " + formatString + " , ");
}
}
/** Lookup all parameter names of the operator with prefix <code>pre</code>.
* If one of the parameters exactly matches <code>pre</code> only this single
* parameter name is returned.
* @return All parameter names with prefix <code>pre</code> or the single parameter
* exactly matching <code>pre</code>
*/
public static LinkedList<String> lookupParameternames( ALDOperator op, String pre ) {
LinkedList<String> names = new LinkedList<String>();
for ( String pName : op.getParameterNames() ) {
if ( pName.startsWith( pre) ) {
names.add( pName);
}
}
if ( names.size() > 1 ) {
// try to find one exact match
for ( String pName : op.getParameterNames() ) {
if ( pName.equals( pre) ) {
names.clear();
names.add( pName);
break;
}
}
}
return names;
}
}