package tcms.API;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.apache.xmlrpc.XmlRpcException;
import com.redhat.qe.xmlrpc.Session;
public abstract class TestopiaObject {
protected Session session;
protected String listMethod;
protected List<Attribute> attributes = new ArrayList<Attribute>();
protected static Logger log = Logger.getLogger(TestopiaObject.class.getName());
protected IntegerAttribute id;
protected Object callXmlrpcMethod(String methodName, Object... params) throws XmlRpcException{
StringBuffer sb = new StringBuffer();
for (Object param:params){
sb.append(param.toString());
sb.append((param==params[params.length-1]?"":","));
}
log.finer("Calling xmlrpc method '" + methodName + "', with params (" + sb.toString() + ")");
Object o = (Object) session.getClient().execute(methodName, params);
//print result for debug purposes
log.finer("Result of '" + methodName + "' = " + deepToString(o));
return o;
}
/**
* Generic method designed to obtain a list of objects that match parameters
* supplies in provided HashMap object
* @param values a Map with the parameters that will be searched for;
* if you supply the pair {"plan_id": 5}, plan_id #5 will be returned. Any combination
* of attributes can be entered and the result will be all matches that fit
* the input values
* @return list of matching objects
*/
public Object[] getList(Map<String, Object> values) throws XmlRpcException
{
//some Testopia objects have no listing mechanism
if(listMethod == null)
return null;
Object[] result = (Object[]) this.callXmlrpcMethod(listMethod, values);
return result;
}
/**
* A simpler method to search by a single parameter that doesn't require creation of a Map object
* @param name - the name of the attribute to search on
* @param value - the value to search for
* @return - list of matching objects
* @throws XmlRpcException
*/
public Object[] getList(String name, Object value) throws XmlRpcException {
Map<String, Object> map = new HashMap<String, Object>();
map.put(name, value);
return getList(map);
}
protected Map<String,Object> getDirtyAttributesMap(){
Map<String,Object> map = new HashMap<String,Object>();
for(Attribute attribute: attributes){
if (attribute.getValue() != null && attribute.isDirty()){
log.finer("Found dirty attribute: "+attribute.getName() + ", value=" + attribute.getValue());
map.put(attribute.getName(), attribute.getValue());
}
}
return map;
}
protected Map<String,Object> getAttributesMap(){
Map<String,Object> map = new HashMap<String,Object>();
for(Attribute attribute: attributes){
if (attribute.getValue() != null){
log.finer("Found attribute: "+attribute.getName() + ", value=" + attribute.getValue());
map.put(attribute.getName(), attribute.getValue());
}
}
return map;
}
protected void syncAttributes(Map remoteMap){
for(Attribute attr: attributes){
String name = attr.getName();
Object val = remoteMap.get(name);
if (name.endsWith("_id")) {
if (val == null) val = remoteMap.get(name.split("_id")[0]);
}
else {
if (val == null) val = remoteMap.get(name + "_id");
}
if (val == null)
try {
log.finer("Did not get attribute " + attr.getName() + " in response.");
}
catch(NullPointerException npe) {}
else {
attr.set(val);
attr.clean();
}
}
}
/**
* protected method to create a new string attribute to use in
* Testopia objects
* @param s the string attribute
* @return
*/
protected StringAttribute newStringAttribute(String name, String value){
StringAttribute sa = new StringAttribute(name, value);
this.attributes.add(sa);
return sa;
}
protected IntegerAttribute newIntegerAttribute(String name, Integer value){
IntegerAttribute ia = new IntegerAttribute(name, value);
this.attributes.add(ia);
return ia;
}
protected BooleanAttribute newBooleanAttribute(String name, Boolean value){
BooleanAttribute ba = new BooleanAttribute(name, value);
this.attributes.add(ba);
return ba;
}
protected void cleanAllAttributes(){
for(Attribute attribute: attributes)
attribute.clean();
}
protected Map<String,Object> updateById(String methodName) throws XmlRpcException{
Map<String,Object> outGoingMap = getDirtyAttributesMap();
Map<String,Object> map;
if (outGoingMap.size() > 0) {
Object o = this.callXmlrpcMethod(methodName, id.get(), outGoingMap);
if (o instanceof Object[]) {
map = (Map<String,Object>)((Object[]) o)[0]; //sometimes map is wrapped in an array
}
else map = (Map<String,Object>)o;
}
else throw new TestopiaException("There are no locally updated fields to update via xmlrpc!");
this.syncAttributes(map);
return map;
}
protected Map<String,Object> create(String methodName) throws XmlRpcException{
Map<String,Object> outGoingMap = getDirtyAttributesMap();
Map<String,Object> map;
if (outGoingMap.size() > 0)
map = (Map<String,Object>)this.callXmlrpcMethod(methodName, outGoingMap);
else throw new TestopiaException("There are no locally updated fields to update via xmlrpc!");
this.syncAttributes(map);
return map;
}
protected Map<String,Object> get(String methodName, Object... params) throws XmlRpcException{
Map<String,Object> map = (Map<String,Object>)this.callXmlrpcMethod(methodName, params);
this.syncAttributes(map);
return map;
}
protected Map<String,Object> getFirstMatching(String methodName, Map params) throws XmlRpcException{
Object firstMatch = ((Object[])this.callXmlrpcMethod(methodName, params))[0];
return (Map<String,Object>)firstMatch;
}
public Integer getId(){
return id.get();
}
abstract class Attribute {
String name = null;
Object value = null;
boolean dirty = true;
public boolean isDirty(){
return dirty;
}
public void clean(){
dirty=false;
}
public Object getValue(){
return value;
}
private void set(Object s){
value = s;
dirty = true;
}
public String getName(){
return name;
}
}
public class StringAttribute extends Attribute{
private StringAttribute(String name, String value){
this.name = name;
this.value = value;
}
public String get(){
return (String)value;
}
public void set(String s){
super.set(s);
}
public String toString(){
return (String)value;
}
}
public class IntegerAttribute extends Attribute{
private IntegerAttribute(String name, Integer value){
this.name = name;
this.value = value;
}
public Integer get(){
return (Integer)value;
}
public void set(Integer s){
super.set(s);
}
}
public class BooleanAttribute extends Attribute{
private BooleanAttribute(String name, Boolean value){
this.name = name;
this.value = value;
}
public Boolean get(){
return (Boolean)value;
}
public void set(Boolean s){
super.set(s);
}
}
public static String deepToString(Object o) {
if (o == null) return "null";
else if (o instanceof Object[]) {
return Arrays.deepToString((Object[])o);
}
else if (o instanceof Map) {
Map m = (Map)o;
StringBuffer b = new StringBuffer();
b.append("{");
for(Object key: m.keySet()) {
b.append(deepToString(key) + ":" + deepToString(m.get(key)) + ", ");
}
b.append("}");
return b.toString();
}
else return o.toString();
}
}