/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* OpenIoT 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
* @author Timotee Maret
* @author Ali Salehi
*/
package org.openiot.gsn.acquisition2.wrappers;
import org.openiot.gsn.beans.AddressBean;
import org.openiot.gsn.beans.DataField;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;
import org.apache.log4j.Logger;
public class MigMessageParameters {
private static Hashtable<Class<?>,String> typesMapping = null;
private final transient Logger logger = Logger.getLogger( MigMessageParameters.class );
private ArrayList<Method> getters = null;
private DataField[] outputStructure = null;
private Method timedFieldGetter = null;
private static final Comparator<Method> orderMethodComparator = new Comparator<Method>() {
public int compare(Method m1, Method m2) { return m1.getName().compareTo(m2.getName()); }
};
private static final Comparator<Method> equalMethodComparator = new Comparator<Method>() {
public int compare(Method m1, Method m2) {
boolean sameName = (m1.getName().compareTo(m2.getName()) == 0);
boolean sameReturnType = (m1.getReturnType().equals(m2.getReturnType()));
if (sameName && sameReturnType) return 0;
else return orderMethodComparator.compare(m1, m2);
}
};
// Optional Parameters
private static final String TINYOS_GETTER_PREFIX = "getter-prefix";
private static final String TINYOS_GETTER_PREFIX_DEFAULT = "get_";
private String tinyosGetterPrefix = null;
private static final String TINYOS_MESSAGE_LENGTH = "message-length";
private int tinyOSMessageLength;
// Mandatory Parameters
private static final String TINYOS_SOURCE = "source";
private String tinyosSource = null;
private static final String TINYOS_MESSAGE_NAME = "message-classname";
private String tinyosMessageName = null;
//private static final String TINYOS_VERSION = "tinyos-version";
public static final byte TINYOS_VERSION_1 = 0x01;
public static final byte TINYOS_VERSION_2 = 0x02;
private byte tinyosVersion = 0;
public void initParameters (AddressBean infos) {
// Mandatory parameters (may thow RuntimeException)
tinyosSource = infos.getPredicateValueWithException(TINYOS_SOURCE) ;
tinyosMessageName = infos.getPredicateValueWithException(TINYOS_MESSAGE_NAME) ;
// Define TinyOS version from the superclasses
try {
Class<?> messageClass = Class.forName(tinyosMessageName);
findTinyOSVersionFromClassHierarchy(messageClass);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to find the >" + tinyosMessageName + "< class.");
}
// Optional parameters
tinyosGetterPrefix = infos.getPredicateValueWithDefault(TINYOS_GETTER_PREFIX, TINYOS_GETTER_PREFIX_DEFAULT);
tinyOSMessageLength = Integer.parseInt(infos.getPredicateValueWithDefault(TINYOS_MESSAGE_LENGTH, "-1")) ;
}
/**
* <p>
* Build recursively the output structure. This implementation use the refelexivity to get the names of the fields.
* The fields which will compose the output structure must hold the following conditions:
* </p>
* <ul>
* <li>The method name must be suffixed either by the specified <code>tinyosGetterPrefix</code> or <code>get_</code> by default.</li>
* <li>The return type must be supprted. See <code>buildMappings</code> method for a list of supported types.</li>
* </ul>
* @param tosmsgClass
* @param fields
*/
public void buildOutputStructure (Class<?> tosmsgClass, ArrayList<DataField> fields, ArrayList<Method> getters) throws RuntimeException {
logger.debug("Building output structure for class: " + tosmsgClass.getCanonicalName() + " and prefix: " + tinyosGetterPrefix);
if (typesMapping == null) buildMappings() ;
boolean tinyos1xMessageClassReached = tosmsgClass == net.tinyos1x.message.Message.class;
boolean tinyos2xMessageClassReached = tosmsgClass == net.tinyos.message.Message.class;
boolean tinyosMessageClassNotFound = tosmsgClass == Object.class;
if (tinyosMessageClassNotFound) {
fields = null;
getters = null;
throw new RuntimeException ("Neither TinyOS1x (net.tinyos1x.message.Message) nor TinyOS2x (net.tinyos.message.Message) message class where found in the >" + tinyosMessageName + "< class hierarchy") ;
}
else if (tinyos1xMessageClassReached || tinyos2xMessageClassReached) {
this.outputStructure = fields.toArray(new DataField[] {});
this.getters = getters;
}
else {
Method[] methods = tosmsgClass.getDeclaredMethods();
Arrays.sort(methods, orderMethodComparator);
Method method = null;
String type = null;
DataField nextField = null;
for (int i = 0 ; i < methods.length ; i++) {
method = methods[i];
// select getters
if (method.getName().startsWith(tinyosGetterPrefix)) {
if ( isInMethodList(getters, method) ) {
logger.warn("The method >" + method.getName() + "< is already defined in a subclass. This getter is skipped.");
}
else if (method.getName().compareToIgnoreCase(tinyosGetterPrefix + "TIMED") == 0) {
logger.warn("next data field is the TIMED field");
timedFieldGetter = method;
}
else {
type = typesMapping.get(method.getReturnType()) ;
if (type == null) {
logger.warn("Not managed type: >" + method.getReturnType() + "< for getter >" + method.getName() + "<. This getter is skipped.");
}
else {
nextField = new DataField (method.getName().substring(tinyosGetterPrefix.length()).toUpperCase() , type) ;
logger.debug("next data field: " + nextField);
fields.add(nextField);
getters.add(method);
}
}
}
}
buildOutputStructure (tosmsgClass.getSuperclass(), fields, getters) ;
}
}
private static boolean isInMethodList (ArrayList<Method> methods, Method amethod) {
Iterator<Method> iter = methods.iterator();
Method nextMethod = null;
while (iter.hasNext()) {
nextMethod = iter.next();
if (equalMethodComparator.compare(nextMethod, amethod) == 0) return true;
}
return false;
}
private static void buildMappings () {
typesMapping = new Hashtable<Class<?>, String> () ;
typesMapping.put(byte.class, "TINYINT") ;
typesMapping.put(short.class, "SMALLINT") ;
typesMapping.put(int.class, "INTEGER") ;
typesMapping.put(long.class, "BIGINT") ;
typesMapping.put(float.class, "DOUBLE");
typesMapping.put(double.class, "DOUBLE");
typesMapping.put(byte[].class, "TINYINT") ;
typesMapping.put(short[].class, "SMALLINT") ;
typesMapping.put(int[].class, "INTEGER") ;
typesMapping.put(long[].class, "BIGINT") ;
typesMapping.put(float[].class, "DOUBLE");
typesMapping.put(double[].class, "DOUBLE");
}
private void findTinyOSVersionFromClassHierarchy (Class<?> messageClass) {
Class<?> currentMessageClass = messageClass;
Class<?> messageSuperClass;
boolean found = false;
while ( ! found ) {
messageSuperClass = currentMessageClass.getSuperclass();
logger.debug("message super class: " + messageSuperClass.getCanonicalName()) ;
if (messageSuperClass == Object.class) break;
else if (messageSuperClass == net.tinyos1x.message.Message.class) {
logger.debug("> TinyOS v1.x message") ;
tinyosVersion = TINYOS_VERSION_1 ;
found = true;
}
else if (messageSuperClass == net.tinyos.message.Message.class) {
tinyosVersion = TINYOS_VERSION_2 ;
logger.debug("> TinyOS v2.x message") ;
found = true;
}
currentMessageClass = messageSuperClass;
}
if (! found) throw new RuntimeException ("Neither TinyOS1x (net.tinyos1x.message.Message) nor TinyOS2x (net.tinyos.message.Message) message class where found in the >" + tinyosMessageName + "< class hierarchy") ;
}
public String getTinyosSource() {
return tinyosSource;
}
public String getTinyosMessageName() {
return tinyosMessageName;
}
public ArrayList<Method> getGetters() {
return getters;
}
public DataField[] getOutputStructure() {
return outputStructure;
}
public String getTinyosGetterPrefix() {
return tinyosGetterPrefix;
}
public byte getTinyosVersion() {
return tinyosVersion;
}
public int getTinyOSMessageLength() {
return tinyOSMessageLength;
}
public Method getTimedFieldGetter() {
return timedFieldGetter;
}
}