/* * 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.operator; import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerXmlbeans; import de.unihalle.informatik.Alida.dataio.provider.xmlbeans.ALDStandardizedDataIOXmlbeans; import de.unihalle.informatik.Alida.dataio.provider.helpers.ALDInstantiationHelper; 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.exceptions.ALDWorkflowException; import de.unihalle.informatik.Alida.annotations.ALDDataIOProvider; import de.unihalle.informatik.Alida.annotations.Parameter.Direction; import de.unihalle.informatik.Alida_xml.ALDXMLKeyValuePairType; import de.unihalle.informatik.Alida_xml.ALDXMLObjectType; import de.unihalle.informatik.Alida_xml.ALDXMLOperatorType; import de.unihalle.informatik.Alida_xml.ALDXMLOperatorWithDescriptorType; import de.unihalle.informatik.Alida_xml.ALDXMLParametrizedType; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.LinkedList; import java.util.HashMap; /** * DataIO provider for parametrized classes and operators from command line. * <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 operators reading is done only for IN and INOUT parameters. * In addition the set of active parameters and descriptors is set as represented in the * xml representation. * * @author posch * */ // Implementation note: this package location is due to access rights @ALDDataIOProvider public class ALDParametrizedClassDataIOXmlbeans extends ALDStandardizedDataIOXmlbeans { /** * 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; } /** Xmlbeans provider for parametrized classes and ALDOperators. * <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>. Additionally the set of active parameters and descriptors is set. * * * @param field * @param cl * @param obj if non null the xml representation is to be parse in this instance. * otherwise a new instance is created. * * @return the object * * @throws ALDDataIOProviderException * @throws ALDDataIOManagerException */ @Override public Object readData(Field field, Class<?> cl, ALDXMLObjectType aldXmlObject, Object obj) throws ALDDataIOProviderException, ALDDataIOManagerException { // in case we have an ALDOperator ALDOperator op = null; if ( cl == null ) { cl = field.getType(); } boolean isOperator; if ( ALDOperator.class.isAssignableFrom(cl) ) { isOperator = true; op = (ALDOperator)obj; } else { isOperator = false; } // initialize object to return if necessary if ( obj == null ) { try { //obj = cl.newInstance(); obj = ALDInstantiationHelper.newInstance( cl); } catch (Exception e ) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.OBJECT_INSTANTIATION_ERROR, "ALDParametrizedClassDataIOXmlbeans::readData cannot instantiate class <" + cl.getCanonicalName() + ">"); } if ( isOperator ) { op = (ALDOperator)obj; // set the name of the operator op.name = ((ALDXMLOperatorType)aldXmlObject).getOpName(); } } if ( debug ) { System.out.println("ALDParametrizedClassDataIOXmlbeans::readData for " + (isOperator ? "an opertor <" + op.getName() + ">" : "a parametrized class: <" + obj.getClass().getName() + ">")); } ALDXMLParametrizedType xmlParametrized = null; try { xmlParametrized = (ALDXMLParametrizedType)aldXmlObject; } catch (Exception e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.OBJECT_TYPE_ERROR, "ALDParametrizedClassDataIOXmlbeans::readData cannot cast xmlObject to ALDXMLParametrizedType"); } // for an operator read and set the active parameters and all associated descriptors form the xml configuration if ( isOperator ) { if ( ALDXMLOperatorWithDescriptorType.class.isAssignableFrom( aldXmlObject.getClass())) { // first deactivate all parameter LinkedList<String> copyOfPnames = new LinkedList<String>(); for ( String pName : op.getParameterNames()) { copyOfPnames.add(pName); } for ( String pName : copyOfPnames) { try { op.removeParameter( pName); } catch (Exception e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParametrizedClassDataIOXmlbeans::readData internal error, cannot remove parameter <" + pName +">"); } } // add parameters from configuration with associated descriptors ALDXMLOperatorWithDescriptorType xmlOperator = (ALDXMLOperatorWithDescriptorType)aldXmlObject; for ( int i = 0 ; i < xmlOperator.getParameterDescriptorsArray().length ; i++ ) { ALDXMLObjectType elementXmlObject = xmlOperator.getParameterDescriptorsArray(i).getValue(); ALDOpParameterDescriptor descr = (ALDOpParameterDescriptor) ALDDataIOManagerXmlbeans.getInstance().readData( null, ALDOpParameterDescriptor.class,elementXmlObject); try { op.addParameter(descr); } catch (ALDOperatorException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParametrizedClassDataIOXmlbeans::readData internal error, cannot add parameter from descriptor <" + descr.name +">"); } } // add remaining parameters from configuration which need to have associated annotated descriptors for ( int i = 0 ; i < xmlOperator.getParameterNamesArray().length ; i++ ) { String pName = xmlOperator.getParameterNamesArray()[i]; if ( ! op.getParameterNames().contains(pName) ) { try { op.addParameter(pName); } catch (ALDOperatorException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParametrizedClassDataIOXmlbeans::readData internal error, cannot add parameter with annotated descriptor <" + pName +">"); } } } } } // convert all name value pairs in the xml object if ( xmlParametrized.getPairsArray().length != 0) { // this map contains all parameters of the operator and associated fields (maybe null) // or all annotated member variables for a parametrized class HashMap<String,Field> fieldMap = null; // initialize fieldmap if ( isOperator) { fieldMap = new HashMap<String, Field>(); for ( int i = 0 ; i < xmlParametrized.getPairsArray().length ; i++ ) { String name = xmlParametrized.getPairsArray(i).getKey(); 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 + ">"); } descr = op.getParameterDescriptor( pName); //if ( descr.getDirection() == Direction.IN || // descr.getDirection() == Direction.INOUT ) { fieldMap.put( name, descr.getField()); //} } catch (Exception e) { // handle this later consistently with parametrized class } } } else { fieldMap = ALDParametrizedClassDataIOHelper.getAnnotatedFields( obj.getClass()); } // now handle the list of name-value pairs for ( int i = 0 ; i < xmlParametrized.getPairsArray().length ; i++ ) { String name = xmlParametrized.getPairsArray(i).getKey(); if ( fieldMap.containsKey( name) ) { Field f = fieldMap.get( name); Object value = null; ALDXMLObjectType elementXmlObject = xmlParametrized.getPairsArray(i).getValue(); if ( elementXmlObject != null) { try { if ( isOperator ) { ALDOpParameterDescriptor descr = op.getParameterDescriptor(name); value = ALDDataIOManagerXmlbeans.getInstance().readData( f, descr.getMyclass(), elementXmlObject); } else { value = ALDDataIOManagerXmlbeans.getInstance().readData( f, null, elementXmlObject); } } catch (ALDDataIOManagerException e) { throw new ALDDataIOManagerException( e.getType(), "ALDParametrizedClassDataIOXmlbeans::readData cannot read element <" + name + "> \n of class <" + f.getType().getCanonicalName() + ">\n from <" + elementXmlObject.toString() + ">" + "\n" + e.getCommentString()); } catch (ALDDataIOProviderException e) { throw new ALDDataIOProviderException( e.getType(), "ALDParametrizedClassDataIOXmlbeans::readData cannot read element <" + name + ">\n of class <" + f.getType().getCanonicalName() + ">\n from <" + elementXmlObject.toString() + ">" + "\n" + e.getCommentString()); } catch (ALDOperatorException e) { throw new ALDDataIOProviderException(ALDDataIOProviderExceptionType.SET_VALUE_FAILED, "ALDParametrizedClassDataIOXmlbeans::readData cannot read descriptor for <" + name + ">\n of class <" + f.getType().getCanonicalName() + ">\n from <" + elementXmlObject.toString() + ">" + "\n" + e.getCommentString()); } } if ( debug ) { System.out.println(" ALDParametrizedClassDataIOXmlbeans::readData set <" + name + "> to value <" + value + ">"); } if ( isOperator) { try { op.setParameter( name, value); } catch (ALDOperatorException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParametrizedClassDataIOXmlbeans::readData internal error, cannot set value of member variable <" + name +">"); } } else { try { ALDParametrizedClassDataIOHelper.setValue( fieldMap.get( name), obj, value); } catch (IllegalAccessException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParametrizedClassDataIOXmlbeans::readData internal error, cannot set value of member variable <" + name +">"); } } } else { // unknown parameter name if ( ! ALDDataIOManagerXmlbeans.getInstance().isAllowAdditionalFields()) { StringBuffer msg = new StringBuffer(" existing parameters:"); for ( String key : fieldMap.keySet() ) msg.append( " " + key); throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.SYNTAX_ERROR, "ALDParametrizedClassDataIOXmlbeans::readData " + (isOperator ? op : obj).getClass().getName() + " does not contain a parameter " + name + new String( msg)); } } } } if ( isOperator) { return op; }else { return obj; } } /** * Transient members are not written. * <p> * For parametrized classes annotated members are written. * <p> * For operators all IN and INOUT parameters are written. * In addition for all parameters the names written and the corresponding * parameter descriptors for not annotated parameters * * @throws ALDDataIOProviderException * @throws ALDDataIOManagerException */ @Override public ALDXMLObjectType writeData(Object obj) throws ALDDataIOProviderException, ALDDataIOManagerException { ALDXMLParametrizedType xmlParametrized; HashMap<String,Field> fieldMap; if ( obj instanceof ALDOperator) { ALDOperator op = ((ALDOperator)obj); if ( debug ) { System.out.println("ALDParametrizedClassDataIOXmlbeans::writeData write an ALDOperator"); } ALDXMLOperatorWithDescriptorType xmlOperator = ALDXMLOperatorWithDescriptorType.Factory.newInstance(); xmlOperator.setOpName(((ALDOperator)obj).getName()); // write all (current/active) parameter names into the xml object // as well as the associated descriptors for non annotated parameters for ( String pName : op.getParameterNames() ) { xmlOperator.addParameterNames(pName); if ( ! op.isAnnotatedParameter( pName) ) { ALDXMLObjectType xmlValue =null; try { xmlValue = ALDDataIOManagerXmlbeans.getInstance().writeData( op.getParameterDescriptor(pName)); } catch (ALDOperatorException e) { // TODO Auto-generated catch block e.printStackTrace(); } ALDXMLKeyValuePairType keyValuePair = xmlOperator.addNewParameterDescriptors(); keyValuePair.setKey(pName); keyValuePair.setValue(xmlValue); } } // collect name of all parameters for which values have to be written xmlParametrized = xmlOperator; // all non-supplemental in and inout parameters Collection<String> pNames = op.getInInoutNames(); // add supplemental in and inout parameters for ( String pName : op.getSupplementalNames() ) { try { if ( op.getParameterDescriptor(pName).getDirection() == Direction.IN || op.getParameterDescriptor(pName).getDirection() == Direction.INOUT) pNames.add( pName); } catch (ALDOperatorException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParametrizedClassDataIOXmlbeans::writeData internal error: can not get descriptor for <" + pName + ">"); } } fieldMap = new HashMap<String, Field>(); for ( String pName : pNames ) { try { fieldMap.put( pName, ((ALDOperator)obj).getParameterDescriptor(pName).getField()); } catch (ALDOperatorException e) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParametrizedClassDataIOXmlbeans::writeData internal error: can not get descriptor for <" + pName + ">"); } } } else { if ( debug ) { System.out.println("ALDParametrizedClassDataIOXmlbeans::writeData write a parametrized class"); } xmlParametrized = ALDXMLParametrizedType.Factory.newInstance(); fieldMap = ALDParametrizedClassDataIOHelper.getAnnotatedFields( obj.getClass()); } // write appropriate parameter values // do not write transient parameters // take care for non member parameters which might occur in ALDOperators for ( String name : fieldMap.keySet() ) { if ( debug ) System.out.println("ALDParametrizedClassDataIOXmlbeans::writeData field <" + name + ">"); Field f = fieldMap.get( name); if ( f == null || (f.getModifiers() & Modifier.TRANSIENT) == 0) { this.addParameter( name, obj, f, "-", xmlParametrized); } } xmlParametrized.setClassName(obj.getClass().getName()); return xmlParametrized; } /** 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 xmlParametrized String buffer to append formated parameter * @throws ALDDataIOProviderException * @throws ALDDataIOManagerException */ private void addParameter( String name, Object obj, Field field, String formatString, ALDXMLParametrizedType xmlParametrized) throws ALDDataIOProviderException, ALDDataIOManagerException { if ( debug ) { System.out.println("ALDParametrizedClassDataIOXmlbeans::addParameter parameter name <" + name + ">"); } Object value = null; try { if ( ALDOperator.class.isAssignableFrom( obj.getClass()) ) { value = ((ALDOperator)obj).getParameter(name); } else { value = ALDParametrizedClassDataIOHelper.getValue( field, obj); } } catch (Exception ex) { throw new ALDDataIOProviderException( ALDDataIOProviderExceptionType.UNSPECIFIED_ERROR, "ALDParametrizedClassDataIOXmlbeans::addParameter internal error, cannot get value of member variable <" + name +">"); } ALDXMLObjectType xmlValue = ALDDataIOManagerXmlbeans.getInstance().writeData(value); ALDXMLKeyValuePairType keyValuePair = xmlParametrized.addNewPairs(); keyValuePair.setKey(name); keyValuePair.setValue(xmlValue); } /** 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; } }