package org.biomoby.shared.extended;

import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.biomoby.client.rdf.vocabulary.DC_PROTEGE;
import org.biomoby.client.rdf.vocabulary.FetaVocabulary;
import org.biomoby.shared.MobyDataType;
import org.biomoby.shared.MobyException;
import org.biomoby.shared.MobyNamespace;
import org.biomoby.shared.MobyPrimaryDataSet;
import org.biomoby.shared.MobyPrimaryDataSimple;
import org.biomoby.shared.MobySecondaryData;
import org.biomoby.shared.MobyService;
import org.biomoby.shared.MobyServiceType;
import org.biomoby.shared.MobyUnitTest;
import org.biomoby.shared.Utils;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.RDFReader;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.shared.JenaException;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
import com.ibm.lsid.LSID;

/**
 * 
 * @author Eddie Kawas
 * @author Wendy Alexander
 * 
 * created Oct 18, 2005 This class parses the RDF document that
 *         describes the service instance ontology.
 *         <p>
 *         An example of how to use this class is below:
 * 
 * <pre><code>
 *               import org.biomoby.shared.MobyService;
 *               import org.biomoby.shared.Utils;
 *               import org.biomoby.shared.extended.ServiceInstanceParser;
 *               
 *               ...
 *                     
 *                          ServiceInstanceParser p = new ServiceInstanceParser(
 *         				&quot;http://biomoby.org/RESOURCES/MOBY-S/ServiceInstances&quot;);
 *         		MobyService[] services = p.getMobyServicesFromRDF();
 *         		if (!p.isRDFValid()) {
 *         			System.out.println(&quot;One or more services in the RDF were invalid&quot;);
 *         			System.out.println(&quot;The errors are the following:&quot;);
 *         			System.out.println(Utils.format(p.getErrors(),2));
 *         			
 *          		}
 *         		System.out.println(&quot;The valid services are:&quot;);
 *         		for (int i = 0; i &lt; services.length; i++) {
 *         			System.out.println((services[i]));
 *         		}
 * </code></pre>
 * 
 * <p>
 * This would output the following a textual description of service instances in
 * the document
 */
public class ServiceInstanceParser {

	private URL url = null;

	private boolean parsedOkay = false;

	private StringBuffer errors = new StringBuffer();

	private String newline = System.getProperty("line.separator");

	/**
	 * Default constructor - need to set the URL for the RDF document that
	 * describes the service ontology using setURL(String url) or setURL(URL
	 * url)
	 * 
	 */
	public ServiceInstanceParser() {
		this.url = null;
		this.parsedOkay = false;
		errors = new StringBuffer();
	}

	/**
	 * Parameterized Constructor that takes in a string argument that is the url
	 * for the RDF document that describes the service ontology.
	 * 
	 * @param url
	 *            a string representation for the URL that resolves to the RDF
	 *            document that describes the service ontology
	 * @throws MobyException
	 *             thrown if the url is malformed
	 */
	public ServiceInstanceParser(String url) throws MobyException {
		try {
			this.url = new URL(url);
		} catch (MalformedURLException e) {
			this.url = null;
			throw new MobyException("Invalid url specified by " + url + ".\n"
					+ e.getLocalizedMessage());
		}
		this.parsedOkay = false;
		errors = new StringBuffer();
	}

	/**
	 * Parameterized Constructor that takes in a url for the RDF document that
	 * describes the service ontology.
	 * 
	 * @param url
	 *            the URL that resolves to the RDF document that describes the
	 *            service ontology
	 * @throws MobyException
	 *             thrown if the url is malformed
	 */
	public ServiceInstanceParser(URL url) throws MobyException {
		if (url != null)
			this.url = url;
		else {
			this.url = null;
			throw new MobyException("Invalid url specified by " + url.toExternalForm() + ".");
		}
		this.parsedOkay = false;
		errors = new StringBuffer();
	}

	/**
	 * 
	 * @return an array of MobyService objects created by parsing the RDF
	 *         document
	 * @throws MobyException
	 *             thrown if the location of the RDF document describing the
	 *             service ontology is invalid or if there is a parsing error
	 */
	public MobyService[] getMobyServicesFromRDF() throws MobyException {

		if (this.url == null) {
			this.parsedOkay = false;
			throw new MobyException(
					"Invalid url specified for the location of the RDF document that describes the Namespace ontology: "
							+ this.url);
		}
		// lets start parsing
		ArrayList<MobyService> list = new ArrayList<MobyService>();
		// create the model
		Model model = ModelFactory.createDefaultModel();
		RDFReader reader = model.getReader();
		try {
			reader.read(model, Utils.getInputStream(getUrl()), null);
		} catch (JenaException e) {
			throw new MobyException(e.getLocalizedMessage());
		} catch (MobyException e) {
		    throw new MobyException(e.getLocalizedMessage());
		}

		this.parsedOkay = processModel(list, model);
		return (MobyService[]) list.toArray(new MobyService[list.size()]);
	}

	/**
	 * 
	 * @param model
	 *            a Model containing the Moby services that you would like to
	 *            extract
	 * @return an array of MobyService objects.
	 * @throws MobyException
	 *             if there is a parsing error
	 */
	public MobyService[] getMobyServicesFromRDF(Model model) throws MobyException {
		// clear out any old information
		ArrayList<MobyService> list = new ArrayList<MobyService>();
		if (model != null) {
			// lets start parsing
			this.parsedOkay = processModel(list, model);
			return (MobyService[]) list.toArray(new MobyService[list.size()]);
		} else {
			return getMobyServicesFromRDF();
		}

	}

       /**
	 * @param list the list to put the services in @param model the model to use
	 */
	private boolean processModel(ArrayList<MobyService> list, Model model) {
		// clear out any old information
		this.parsedOkay = false;
		this.errors = new StringBuffer();

		boolean success = true;
		ResIterator rIterator = model.listSubjectsWithProperty(FetaVocabulary.hasServiceNameText);
		while (rIterator.hasNext()) {
			boolean currentlyBad = false;
			Resource resource = rIterator.nextResource();

			if (resource.getURI() == null || resource.getURI().equals(RDFS.comment.getURI())
					|| resource.getURI().equals(RDFS.label.getURI()))
				continue;

			String name = "";
			if (resource.hasProperty(FetaVocabulary.hasServiceNameText))
				name = resource.getProperty(FetaVocabulary.hasServiceNameText).getObject()
						.toString();
			MobyService service = new MobyService(name);

			// get the signatureURL
			String signatureURL = "";
			if (resource.hasProperty(FetaVocabulary.hasServiceDescriptionLocation))
				signatureURL = resource.getProperty(FetaVocabulary.hasServiceDescriptionLocation)
						.getObject().toString();
			service.setSignatureURL(signatureURL);
			// set the service type
			if (resource.hasProperty(FetaVocabulary.hasOperation)) {
				Resource hoResource = resource.getProperty(FetaVocabulary.hasOperation)
						.getResource();
				if (hoResource.hasProperty(FetaVocabulary.performsTask)) {
					Resource ptResource = hoResource.getProperty(FetaVocabulary.performsTask)
							.getResource();
					StmtIterator stmtIterator = ptResource.listProperties(RDF.type);
					while (stmtIterator.hasNext()) {
						RDFNode typeNode = stmtIterator.nextStatement().getObject();
						if (!typeNode.toString().equals(FetaVocabulary.operationTask.toString())) {
							String serviceType = typeNode.toString();
							try {
								LSID serviceTypeLsid = new LSID(serviceType);
								MobyServiceType mst = new MobyServiceType(serviceTypeLsid
										.getObject());
								mst.setLSID(serviceTypeLsid.toString());
								service.setServiceType(mst);
							} catch (Exception e) {
								// wasn't an lsid, so parse the URI
								String[] parsedServiceType = serviceType.split("#");
								if (parsedServiceType.length == 2)
									service.setType(parsedServiceType[1]);
								else if(serviceType.lastIndexOf("/Services/") > 0) {
								    service.setType(serviceType.substring(serviceType.lastIndexOf("/Services/")+"/Services/".length()));
								} else 
								    service.setType(parsedServiceType[0]);
							}
						}
					}
				}
				
				// get/set the unit test information for the service
				if (hoResource.hasProperty(FetaVocabulary.hasUnitTest)) {
				    NodeIterator ni = model.listObjectsOfProperty(hoResource, FetaVocabulary.hasUnitTest);
				    while (ni.hasNext()) {
					RDFNode rdfNode = ni.nextNode();
					if (!rdfNode.isResource())
					    continue;
					Resource node = (Resource)rdfNode;
					MobyUnitTest mut = new MobyUnitTest();
					    if (node.hasProperty(FetaVocabulary.exampleInput)) 
						mut.setExampleInput(node.getProperty(FetaVocabulary.exampleInput).getObject().toString());
					    if (node.hasProperty(FetaVocabulary.validOutputXML)) 
						mut.setValidOutputXML(node.getProperty(FetaVocabulary.validOutputXML).getObject().toString());
					    if (node.hasProperty(FetaVocabulary.validXPath)) 
						mut.setValidXPath(node.getProperty(FetaVocabulary.validXPath).getObject().toString());
					    if (node.hasProperty(FetaVocabulary.validREGEX)) 
						mut.setValidREGEX(node.getProperty(FetaVocabulary.validREGEX).getObject().toString());
					    service.addUnitTest(mut);
				    }
				}

			}

			// set the category
			if (resource.hasProperty(DC_PROTEGE.format)) {
				String category = resource.getProperty(DC_PROTEGE.format).getObject().toString();
				service.setCategory(category);
			}
			// set the lsid
			if (resource.hasProperty(DC_PROTEGE.identifier))
				service.setLSID(resource.getProperty(DC_PROTEGE.identifier).getObject().toString());
			// set the authority, email address and authoritative values
			if (resource.hasProperty(FetaVocabulary.providedBy)) {
				Resource org = (Resource) resource.getProperty(FetaVocabulary.providedBy)
						.getObject();
				if (org.hasProperty(DC_PROTEGE.publisher)) {
					String authorityURI = org.getProperty(DC_PROTEGE.publisher).getObject()
							.toString();
					service.setAuthority(authorityURI);
				}
				if (org.hasProperty(DC_PROTEGE.creator)) {
					String email = org.getProperty(DC_PROTEGE.creator).getObject().toString();
					service.setEmailContact(email);
				}
				if (org.hasProperty(FetaVocabulary.authoritative)) {
					String authoritative = org.getProperty(
							FetaVocabulary.authoritative).getObject().toString()
							.trim();
					if (authoritative.equalsIgnoreCase("true"))
						service.setAuthoritative(true);
					else
						service.setAuthoritative(false);
				}
			}

			// set the url for the service
			if (resource.hasProperty(FetaVocabulary.locationURI)) {
				String serviceUrl = resource.getProperty(FetaVocabulary.locationURI).getObject()
						.toString();
				service.setURL(serviceUrl);
			}
			// set the description
			if (resource.hasProperty(FetaVocabulary.hasServiceDescriptionText)) {
				String description = resource.getProperty(FetaVocabulary.hasServiceDescriptionText)
						.getObject().toString();
				service.setDescription(description);
			}
			// see if the service is working
			if (resource.hasProperty(FetaVocabulary.isAlive)) {
				String bool = resource.getProperty(FetaVocabulary.isAlive)
						.getObject().toString();
				service.setStatus(MobyService.ALIVE, "true".equals(bool));
			}

			// process in/outputs
			StmtIterator arguements = resource.listProperties(FetaVocabulary.hasOperation);
			while (arguements.hasNext()) {
				Statement s = arguements.nextStatement();
				Resource r = (Resource) s.getObject();
				StmtIterator inputs = r.listProperties(FetaVocabulary.inputParameter);
				// collections to be added at the end the current scope
				Map<String,MobyPrimaryDataSet> inputCollectionMap = new HashMap<String, MobyPrimaryDataSet>();
				Map<String, MobyPrimaryDataSet> outputCollectionMap = new HashMap<String, MobyPrimaryDataSet>();
				// is the current service bad?
				while (inputs.hasNext()) {

					Resource parameter = (Resource) inputs.nextStatement().getObject();
					if (!parameter.getProperty(FetaVocabulary.hasParameterType).getResource()
							.hasProperty(RDF.type)) {
						success = false;
						currentlyBad = true;
						errors.append(service.getName() + "," + service.getAuthority()
								+ "{Input for service " + service.getName()
								+ " did not have a parameter type of type RDF:type}");
						errors.append(newline);
						continue;
					} else {
						if (parameter.getProperty(FetaVocabulary.hasParameterType).getResource()
								.getProperty(RDF.type).getObject().toString().endsWith(
										"simpleParameter")) {
							// we have a simple
							// make sure that object type and article name exist
							if (!parameter.hasProperty(FetaVocabulary.object_type)) {
								success = false;
								currentlyBad = true;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Input for service " + service.getName()
										+ " was missing an object type}");
								errors.append(newline);
								continue;
							}
							if (!parameter.hasProperty(FetaVocabulary.hasParameterNameText)) {
								currentlyBad = true;
								success = false;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Input for service " + service.getName()
										+ " was missing an article name}");
								errors.append(newline);
								continue;
							}
							
							RDFNode rdfDatatype = parameter.getProperty(
									FetaVocabulary.object_type).getObject();
							
							String datatype = "";
							if (rdfDatatype instanceof Resource) {
								if (((Resource)rdfDatatype).hasProperty(RDF.type))
									datatype = ((Resource)rdfDatatype).getProperty(RDF.type).getObject().toString();
								else {
									success = false;
									currentlyBad = true;
									errors.append(service.getName() + "," + service.getAuthority()
											+ "{Input for service " + service.getName()
											+ " was missing an object type}");
									errors.append(newline);
									continue;
								}
							}

							if (datatype.indexOf("#") > 0)
								datatype = datatype.substring(datatype.indexOf("#") + 1);
							else if(datatype.lastIndexOf("/Objects/") > 0) {
							    datatype = datatype.substring(datatype.lastIndexOf("/Objects/")+"/Objects/".length());
							}
							// extract the article name
							String articlename = parameter.getProperty(
									FetaVocabulary.hasParameterNameText).getObject().toString();
							if (articlename.equals("")) {
								success = false;
								currentlyBad = true;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Input for service " + service.getName()
										+ " was missing a non empty article name}");
								errors.append(newline);
								continue;
							}
							// check for namespaces
							ArrayList<String> namespaces = new ArrayList<String>();
							if (parameter.hasProperty(FetaVocabulary.inNamespaces)) {
								StmtIterator namespaceIterator = parameter.listProperties(FetaVocabulary.inNamespaces);
								while (namespaceIterator.hasNext()) {
									RDFNode namespace = namespaceIterator.nextStatement().getObject();
									if (namespace instanceof Resource) {
										StmtIterator typeIterator = ((Resource)namespace).listProperties(RDF.type);
										while (typeIterator.hasNext()) {
											String ns = typeIterator.nextStatement()
													.getObject().toString();
											if (ns.equals(FetaVocabulary.parameterNamespace.toString()))
												continue;
											if (ns.indexOf("#") > 0)
												ns = ns.substring(ns.indexOf("#") + 1);
											else if(ns.lastIndexOf("/Namespaces/") > 0) {
											    ns = ns.substring(ns.lastIndexOf("/Namespaces/")+"/Namespaces/".length());
											}
											namespaces.add(ns);
										}
									}
									
								}
							}
							MobyPrimaryDataSimple primaryInput = new MobyPrimaryDataSimple();
							primaryInput.setDataType(new MobyDataType(datatype));
							primaryInput.setName(articlename);
							if (!namespaces.isEmpty()) {
								for (String ns : namespaces) {
									primaryInput.addNamespace(new MobyNamespace(ns));
								}
							}
							service.addInput(primaryInput);
						} else if (parameter.getProperty(FetaVocabulary.hasParameterType)
								.getResource().getProperty(RDF.type).getObject().toString()
								.endsWith("collectionParameter")) {
							// we have a collection
							// make sure that object type and article name exist
							if (!parameter.hasProperty(FetaVocabulary.object_type)) {
								currentlyBad = true;
								success = false;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Collection input for service " + service.getName()
										+ " was missing an object type.}");
								errors.append(newline);
								continue;
							}
							if (!parameter.hasProperty(FetaVocabulary.hasParameterNameText)) {
								success = false;
								currentlyBad = true;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Collection for service " + service.getName()
										+ " was missing an article name.}");
								errors.append(newline);
								continue;
							}
							// name
							/*String datatype = parameter.getProperty(
									FetaVocabulary.object_type).getObject()
									.toString();*/
							RDFNode rdfDatatype = parameter.getProperty(
									FetaVocabulary.object_type).getObject();
							
							String datatype = "";
							if (rdfDatatype instanceof Resource) {
								if (((Resource)rdfDatatype).hasProperty(RDF.type))
									datatype = ((Resource)rdfDatatype).getProperty(RDF.type).getObject().toString();
								else {
									success = false;
									currentlyBad = true;
									errors.append(service.getName() + "," + service.getAuthority()
											+ "{Input collection for service " + service.getName()
											+ " was missing an object type}");
									errors.append(newline);
									continue;
								}
							}
							if (datatype.indexOf("#") > 0)
								datatype = datatype.substring(datatype.indexOf("#") + 1);
							else if(datatype.lastIndexOf("/Objects/") > 0) {
							    datatype = datatype.substring(datatype.lastIndexOf("/Objects/")+"/Objects/".length());
							}

							// extract the article name
							String articlename = parameter.getProperty(
									FetaVocabulary.hasParameterNameText).getObject().toString();
							if (articlename.equals("")) {
								success = false;
								currentlyBad = true;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Collection input for service " + service.getName()
										+ " was missing a non empty article name.}");
								errors.append(newline);
								continue;
							}

							// check for namespaces
							ArrayList<String> namespaces = new ArrayList<String>();
							if (parameter.hasProperty(FetaVocabulary.inNamespaces)) {
								StmtIterator namespaceIterator = parameter.listProperties(FetaVocabulary.inNamespaces);
								while (namespaceIterator.hasNext()) {
									RDFNode namespace = namespaceIterator.nextStatement().getObject();
									if (namespace instanceof Resource) {
										StmtIterator typeIterator = ((Resource)namespace).listProperties(RDF.type);
										while (typeIterator.hasNext()) {
											String ns = typeIterator.nextStatement()
													.getObject().toString();
											if (ns.equals(FetaVocabulary.parameterNamespace.toString()))
												continue;
											if (ns.indexOf("#") > 0)
												ns = ns.substring(ns.indexOf("#") + 1);
											else if(ns.lastIndexOf("/Namespaces/") > 0) {
											    ns = ns.substring(ns.lastIndexOf("/Namespaces/")+"/Namespaces/".length());
											}
											namespaces.add(ns);
										}
									}
									
								}
							}

							MobyPrimaryDataSet collection = null;
							if (inputCollectionMap.containsKey(articlename)) {
								collection = (MobyPrimaryDataSet) inputCollectionMap
										.get(articlename);
							} else
								collection = new MobyPrimaryDataSet(articlename);
							MobyPrimaryDataSimple input = new MobyPrimaryDataSimple("");
							input.setDataType(new MobyDataType(datatype));
							if (!namespaces.isEmpty()) {
								for (String ns : namespaces) {
									input
											.addNamespace(new MobyNamespace(ns));
								}
							}
							collection.addElement(input);
							inputCollectionMap.put(articlename, collection);
						} else if (parameter.getProperty(FetaVocabulary.hasParameterType)
								.getResource().getProperty(RDF.type).getObject().toString()
								.endsWith("secondaryParameter")) {
							// we have a secondary
							if (!parameter.hasProperty(FetaVocabulary.datatype)) {
								currentlyBad = true;
								success = false;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Invalid secondary input found. Missing moby datatype"
										+ " (one of String, Float, Integer, DateTime).}");
								errors.append(newline);
								continue;
							}
							if (!parameter.hasProperty(FetaVocabulary.hasParameterNameText)) {
								success = false;
								currentlyBad = true;
								errors
										.append(service.getName()
												+ ","
												+ service.getAuthority()
												+ "{Invalid secondary input found. Missing an article name.}");
								errors.append(newline);
								continue;
							}
							String datatype = parameter.getProperty(
									FetaVocabulary.datatype).getLiteral().getValue()
									.toString();
							if (datatype.indexOf("#") > 0)
								datatype = datatype.substring(datatype.indexOf("#") + 1);
							if (datatype.indexOf(":") > 0)
								datatype = datatype.substring(datatype.lastIndexOf(":") + 1);
							// extract the article name
							String articlename = parameter.getProperty(
									FetaVocabulary.hasParameterNameText).getLiteral().getValue()
									.toString();
							if (articlename.equals("")) {
								success = false;
								currentlyBad = true;
								errors
										.append(service.getName()
												+ ","
												+ service.getAuthority()
												+ "{Invalid secondary input found. Missing a non empty article name.}");
								errors.append(newline);
								continue;
							}

							// create the datatype
							MobySecondaryData secondary = new MobySecondaryData(articlename);
							try{
                                                            secondary.setDataType(datatype);
                                                        } catch(Exception e){
                                                            e.printStackTrace(); // will default to String if unrecognized
                                                        }

							if (parameter.hasProperty(FetaVocabulary.hasDefaultValue)) {
								secondary.setDefaultValue(parameter.getProperty(
										FetaVocabulary.hasDefaultValue).getLiteral().getValue()
										.toString());
							}
							// process the description
							if (parameter
									.hasProperty(FetaVocabulary.hasParameterDescriptionText)) {
								secondary.setDescription(parameter.getProperty(
										FetaVocabulary.hasParameterDescriptionText)
										.getLiteral().getValue().toString());
							}

							if (parameter.hasProperty(FetaVocabulary.max)) {
								try {
									secondary.setMaxValue(parameter
											.getProperty(FetaVocabulary.max)
											.getLiteral().getValue().toString());
								} catch (NumberFormatException e) {
									success = false;
									currentlyBad = true;
									errors.append(service.getName() + "," + service.getAuthority()
											+ "{Invalid maximum value for secondary input.}");
									errors.append(newline);
									continue;
								}
							}
							if (parameter.hasProperty(FetaVocabulary.min)) {
								try {
									secondary.setMinValue(parameter
											.getProperty(FetaVocabulary.min)
											.getLiteral().getValue().toString());
								} catch (NumberFormatException e) {
									success = false;
									currentlyBad = true;
									errors.append(service.getName() + "," + service.getAuthority()
											+ "{Invalid minimum value for secondary input.}");
									errors.append(newline);
									continue;
								}
							}
							if (parameter.hasProperty(FetaVocabulary.enumeration)) {
								StmtIterator enumerations = parameter
										.listProperties(FetaVocabulary.enumeration);
								while (enumerations.hasNext()) {
									secondary.addAllowedValue(enumerations.nextStatement()
											.getLiteral().getValue().toString());
								}
							}
							service.addInput(secondary);
						} else {
							// everything else is ignored.
						}
					}
				}

				StmtIterator outputs = r.listProperties(FetaVocabulary.outputParameter);
				while (outputs.hasNext()) {
					Resource parameter = (Resource) outputs.nextStatement().getObject();
					if (!parameter.getProperty(FetaVocabulary.hasParameterType).getResource()
							.hasProperty(RDF.type)) {
						success = false;
						currentlyBad = true;
						errors.append(service.getName() + "," + service.getAuthority()
								+ "{Output for service " + service.getName()
								+ " did not have a parameter type of type RDF:type}");
						errors.append(newline);
						continue;
					} else {
						if (parameter.getProperty(FetaVocabulary.hasParameterType).getResource()
								.getProperty(RDF.type).getObject().toString().endsWith(
										"simpleParameter")) {
							// we have a simple
							// make sure that object type and article name exist
							if (!parameter.hasProperty(FetaVocabulary.object_type)) {
								currentlyBad = true;
								success = false;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Output for service " + service.getName()
										+ " was missing an object type.}");
								errors.append(newline);
								continue;
							}
							if (!parameter.hasProperty(FetaVocabulary.hasParameterNameText)) {
								success = false;
								currentlyBad = true;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Output for service " + service.getName()
										+ " was missing an article name}");
								errors.append(newline);
								continue;
							}

							// name
							RDFNode rdfDatatype = parameter.getProperty(
									FetaVocabulary.object_type).getObject();
							
							String datatype = "";
							if (rdfDatatype instanceof Resource) {
								if (((Resource)rdfDatatype).hasProperty(RDF.type))
									datatype = ((Resource)rdfDatatype).getProperty(RDF.type).getObject().toString();
								else {
									success = false;
									currentlyBad = true;
									errors.append(service.getName() + "," + service.getAuthority()
											+ "{Output for service " + service.getName()
											+ " was missing an object type}");
									errors.append(newline);
									continue;
								}
							}
							
							/*String datatype = parameter.getProperty(
									FetaVocabulary.object_type).getObject()
									.toString();*/
							if (datatype.indexOf("#") > 0)
								datatype = datatype.substring(datatype.indexOf("#") + 1);
							else if(datatype.lastIndexOf("/Objects/") > 0) {
							    datatype = datatype.substring(datatype.lastIndexOf("/Objects/")+"/Objects/".length());
							}
							// extract the article name
							String articlename = parameter.getProperty(
									FetaVocabulary.hasParameterNameText).getObject().toString();
							if (articlename.equals("")) {
								// throw new MobyException("Output for service "
								// +
								// service.getName() + " was missing a non empty
								// article name");
							}
							// check for namespaces
							ArrayList<String> namespaces = new ArrayList<String>();
							if (parameter.hasProperty(FetaVocabulary.inNamespaces)) {
								StmtIterator namespaceIterator = parameter.listProperties(FetaVocabulary.inNamespaces);
								while (namespaceIterator.hasNext()) {
									RDFNode namespace = namespaceIterator.nextStatement().getObject();
									if (namespace instanceof Resource) {
										StmtIterator typeIterator = ((Resource)namespace).listProperties(RDF.type);
										while (typeIterator.hasNext()) {
											String ns = typeIterator.nextStatement()
													.getObject().toString();
											if (ns.equals(FetaVocabulary.parameterNamespace.toString()))
												continue;
											if (ns.indexOf("#") > 0)
												ns = ns.substring(ns.indexOf("#") + 1);
											else if(ns.lastIndexOf("/Namespaces/") > 0) {
											    ns = ns.substring(ns.lastIndexOf("/Namespaces/")+"/Namespaces/".length());
											}
											namespaces.add(ns);
										}
									}
									
								}
							}
							MobyPrimaryDataSimple primaryOutput = new MobyPrimaryDataSimple();
							primaryOutput.setDataType(new MobyDataType(datatype));
							primaryOutput.setName(articlename);
							if (!namespaces.isEmpty()) {
								for (String ns : namespaces) {
									primaryOutput.addNamespace(new MobyNamespace(ns));
								}
							}
							service.addOutput(primaryOutput);
						} else if (parameter.getProperty(FetaVocabulary.hasParameterType)
								.getResource().getProperty(RDF.type).getObject().toString()
								.endsWith("collectionParameter")) {
							// we have a collection
							// make sure that object type and article name exist
							if (!parameter.hasProperty(FetaVocabulary.object_type)) {
								success = false;
								currentlyBad = true;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Collection Output for service " + service.getName()
										+ " was missing an object type.}");
								errors.append(newline);
								continue;
							}
							if (!parameter.hasProperty(FetaVocabulary.hasParameterNameText)) {
								success = false;
								currentlyBad = true;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Collection for service " + service.getName()
										+ " was missing an article name.}");
								errors.append(newline);
								continue;
							}
							// name
							/*String datatype = parameter.getProperty(
									FetaVocabulary.object_type).getObject()
									.toString();
							*/
							RDFNode rdfDatatype = parameter.getProperty(
									FetaVocabulary.object_type).getObject();
							String datatype = "";
							if (rdfDatatype instanceof Resource) {
								if (((Resource)rdfDatatype).hasProperty(RDF.type))
									datatype = ((Resource)rdfDatatype).getProperty(RDF.type).getObject().toString();
								else {
									success = false;
									currentlyBad = true;
									errors.append(service.getName() + "," + service.getAuthority()
											+ "{Output collection for service " + service.getName()
											+ " was missing an object type}");
									errors.append(newline);
									continue;
								}
							}
							
							if (datatype.indexOf("#") > 0)
								datatype = datatype.substring(datatype.indexOf("#") + 1);
							else if(datatype.lastIndexOf("/Objects/") > 0) {
							    datatype = datatype.substring(datatype.lastIndexOf("/Objects/")+"/Objects/".length());
							}
							// extract the article name
							String articlename = parameter.getProperty(
									FetaVocabulary.hasParameterNameText).getObject().toString();
							if (articlename.equals("")) {
								success = false;
								currentlyBad = true;
								errors.append(service.getName() + "," + service.getAuthority()
										+ "{Collection output for service " + service.getName()
										+ " was missing a non empty article name.}");
								errors.append(newline);
								continue;

							}

							// check for namespaces
							ArrayList<String> namespaces = new ArrayList<String>();
							if (parameter.hasProperty(FetaVocabulary.inNamespaces)) {
								StmtIterator namespaceIterator = parameter.listProperties(FetaVocabulary.inNamespaces);
								while (namespaceIterator.hasNext()) {
									RDFNode namespace = namespaceIterator.nextStatement().getObject();
									if (namespace instanceof Resource) {
										StmtIterator typeIterator = ((Resource)namespace).listProperties(RDF.type);
										while (typeIterator.hasNext()) {
											String ns = typeIterator.nextStatement()
													.getObject().toString();
											if (ns.equals(FetaVocabulary.parameterNamespace.toString()))
												continue;
											if (ns.indexOf("#") > 0)
												ns = ns.substring(ns.indexOf("#") + 1);
											else if(ns.lastIndexOf("/Namespaces/") > 0) {
											    ns= ns.substring(ns.lastIndexOf("/Namespaces/")+"/Namespaces/".length());
											}
											namespaces.add(ns);
										}
									}
									
								}
							}

							MobyPrimaryDataSet collection = null;
							if (outputCollectionMap.containsKey(articlename)) {
								collection = (MobyPrimaryDataSet) outputCollectionMap
										.get(articlename);
							} else
								collection = new MobyPrimaryDataSet(articlename);
							MobyPrimaryDataSimple output = new MobyPrimaryDataSimple("");
							output.setDataType(new MobyDataType(datatype));
							if (!namespaces.isEmpty()) {
								for (String ns : namespaces) {
									output.addNamespace(new MobyNamespace(ns));
								}
							}
							collection.addElement(output);
							outputCollectionMap.put(articlename, collection);
						}
					}
				}

				// add the collections to the service
				for (String key : inputCollectionMap.keySet()) {
					service.addInput((MobyPrimaryDataSet) inputCollectionMap.get(key));
				}
				for (String key : outputCollectionMap.keySet()) {
					service.addOutput((MobyPrimaryDataSet) outputCollectionMap.get(key));
				}
			}
			if (!currentlyBad)
				list.add(service);
		}
		return success;
	}

	/**
	 * 
	 * @param rdf
	 *            a valid RDF document to be parsed into moby services
	 * @return an array of MobyService objects
	 * @throws MobyException
	 *             if there are any parsing errors
	 */
	public MobyService[] getMobyServicesFromRDF(String rdf) throws MobyException {

		// lets start parsing
		ArrayList<MobyService> list = new ArrayList<MobyService>();
		// create the model
		Model model = ModelFactory.createDefaultModel();
		RDFReader reader = model.getReader();
		try {
			reader.read(model, new StringReader(rdf), null);
		} catch (JenaException e) {
			this.parsedOkay = false;
			throw new MobyException(e.getLocalizedMessage());
		}
		this.parsedOkay = processModel(list, model);
		return (MobyService[]) list.toArray(new MobyService[list.size()]);
	}

	/**
	 * 
	 * @param url
	 *            the URL that resolves to the RDF document that describes the
	 *            service ontology
	 * @throws MobyException
	 *             thrown if the url is malformed
	 */
	public void setUrl(URL url) throws MobyException {
		if (url != null)
			this.url = url;
		else {
			this.url = null;
			throw new MobyException("Invalid url specified by " + url.toExternalForm() + ".");
		}
	}

	/**
	 * 
	 * @param url
	 *            the string represention of the URL that resolves to the RDF
	 *            document that describes the service ontology.
	 * @throws MobyException
	 *             thrown if the url is malformed
	 */
	public void setUrl(String url) throws MobyException {
		try {
			this.url = new URL(url);
		} catch (MalformedURLException e) {
			this.url = null;
			throw new MobyException("Invalid url specified by " + url + ".\n"
					+ e.getLocalizedMessage());
		}
	}

	/**
	 * 
	 * @return the url used to retrieve the RDF document that describes the
	 *         service ontology.
	 */
	public URL getUrl() {
		return url;
	}

	/**
	 * Method to check whether an RDF document contained only valid services.
	 * 
	 * @return true if the last rdf document contained no invalid services,
	 *         false otherwise
	 */
	public boolean isRDFValid() {
		return parsedOkay;
	}

	/**
	 * 
	 * @return a string containing any errors obtained by parsing the RDF
	 *         document. These errors are service specific errors, like unamed
	 *         inputs, etc.
	 */
	public String getErrors() {
		return errors.toString();
	}

	public static void main(String[] args) throws MobyException, MalformedURLException, IOException {
		// show how to use this class
		ServiceInstanceParser p = new ServiceInstanceParser("http://dev.biordf.net/~kawas/signatures/unittests/BIND_IdSearchGetFastaByCdd");
		p.setUrl("file:///C:/Users/Eddie/Desktop/BIND_IdSearchGetFastaByCdd.txt");
		MobyService[] services = p.getMobyServicesFromRDF();
		System.out.println(services.length);
		if (!p.isRDFValid()) {
			System.out.println("One or more services in the RDF were invalid");
			System.out.println("The errors are the following:");
			System.out.println(Utils.format(p.getErrors(), 2));

		}
		System.out.println("The valid services are:");
		for (int i = 0; i < services.length; i++) {
			System.out.println((services[i].toString()));
		}
	}
}
