001/* 002 * Copyright (c) 2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: welker $' 006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 007 * '$Revision: 24234 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.ecoinformatics.seek.taxon; 031 032import java.net.MalformedURLException; 033import java.rmi.RemoteException; 034 035import javax.xml.rpc.ServiceException; 036 037import org.ecoinformatics.taxon.client.CSOAPClient; 038import org.ecoinformatics.taxon.soap.holders.FloatArrayHolder; 039import org.ecoinformatics.taxon.soap.holders.StringArrayHolder; 040 041import ptolemy.actor.TypedAtomicActor; 042import ptolemy.actor.TypedIOPort; 043import ptolemy.data.ArrayToken; 044import ptolemy.data.BooleanToken; 045import ptolemy.data.StringToken; 046import ptolemy.data.expr.Parameter; 047import ptolemy.data.expr.StringParameter; 048import ptolemy.data.type.ArrayType; 049import ptolemy.data.type.BaseType; 050import ptolemy.kernel.CompositeEntity; 051import ptolemy.kernel.util.IllegalActionException; 052import ptolemy.kernel.util.NameDuplicationException; 053 054/** 055 * The GetTaxa actor provides access to taxonomic data via the Taxonomic Object 056 * Service. (TOS). 057 * <p> 058 * The Taxonomic Object Service is a web services enabled product that allows 059 * user to query taxonomic data in a variety of ways, for example, name to 060 * concept resolution, walking a classification tree and finding concepts 061 * related to one another. 062 * </p> 063 * <p> 064 * This actor uses several of the API methods defined within the TOS WSDL (@todo 065 * URL to WSDL) to find all syunonymous names defined within a particular 066 * authority at a user specified rank where each of those names are descendants 067 * of a specified concept. For example, this actor can be used to find all names 068 * at the species level associated with the ITIS, 2006 concept of Mammalia. 069 * </p> 070 * <p> 071 * The user of the actor must provide the authority, root concept's name and the 072 * target level. After execution, the return will be a two-dimensional array 073 * containing with the following structure: <code> 074 * [ ['guid1', 'name1', name2', ...], ['guid2', 'name1', 'name2', ...]...] 075 * </code> The first element of each row 076 * contains the GUID of the concept to which the following names are associated. 077 * This data will presented to downstream actors on the 078 * <code>synonymousNames</code> port. 079 * </p> 080 * <p> 081 * Any errors that take place during configuration or execution of the actor 082 * will be concatenated into a single string and presented on the 083 * <code>clientExecErrors</code> port. 084 * </p> 085 */ 086public class GetTaxa extends TypedAtomicActor { 087 /* constants */ 088 private static final String URL = "http://seek.nhm.ku.edu/TaxObjServ/services/TaxonomicObjectServicePort"; 089 090 /* dialog parameters */ 091 public StringParameter taxaName = null; 092 public StringParameter authority = null; 093 public StringParameter targetLevel = null; 094 public Parameter checkForOverlap = null; 095 096 /* ports */ 097 public TypedIOPort synonymousNames = null; 098 public TypedIOPort clientExecErrors = null; 099 100 /* private members */ 101 private StringBuffer errBuf = new StringBuffer(); 102 103 /* @todo document me */ 104 public GetTaxa(CompositeEntity container, String name) 105 throws NameDuplicationException, IllegalActionException { 106 107 super(container, name); 108 109 /* configure parameters */ 110 taxaName = new StringParameter(this, "taxaName"); 111 authority = new StringParameter(this, "authority"); 112 targetLevel = new StringParameter(this, "targetLevel"); 113 checkForOverlap = new Parameter(this, "checkForOverlap"); 114 checkForOverlap.setTypeEquals(BaseType.BOOLEAN); 115 checkForOverlap.setToken(BooleanToken.FALSE); 116 117 try { 118 String[] authorities = callGetAuthorityNames(); 119 for (int i = 0; i < authorities.length; i++) { 120 authority.addChoice(authorities[i]); 121 } 122 } catch (Exception e) { 123 _debug("<EXCEPTION> There was an error trying to retrieve the authorities " 124 + "from the TOS. Using a plain text field for authority. " 125 + e + ". </EXCEPTION>"); 126 } 127 128 /* configure ports */ 129 synonymousNames = new TypedIOPort(this, "synonymousNames", false, true); 130 synonymousNames.setTypeEquals(new ArrayType(new ArrayType( 131 BaseType.STRING))); 132 clientExecErrors = new TypedIOPort(this, "clientExecErrors", false, 133 true); 134 clientExecErrors.setTypeEquals(BaseType.STRING); 135 136 /* icon */ 137 _attachText( 138 "_iconDescription", 139 "<svg>\n<rect x=\"0\" y=\"0\" width=\"60\" height=\"30\" style=\"fill:white\"/>\n</svg>\n"); 140 } 141 142 /* @todo document me */ 143 public void fire() throws IllegalActionException { 144 super.fire(); 145 146 String name = ((StringToken) taxaName.getToken()).stringValue(); 147 String auth = ((StringToken) authority.getToken()).stringValue(); 148 String level = ((StringToken) targetLevel.getToken()).stringValue(); 149 boolean checkOverlaps = ((BooleanToken) checkForOverlap.getToken()) 150 .booleanValue(); 151 152 /* @todo error if no results from get best concept */ 153 try { 154 String[] guids = this.callGetBestConcept(name, auth); 155 156 if (guids == null || guids.length == 0) { 157 this.errBuf.append("No concepts could be found for '").append( 158 name).append("' within authority '").append(auth) 159 .append("'."); 160 } 161 162 int indexSelected = 0; 163 164 if (guids.length > 1) { 165 /* @todo interact with user */ 166 } 167 168 String[][] synNames = this.callGetSynsForAuthoritativeList( 169 guids[indexSelected], auth, level); 170 171 if (checkOverlaps) { 172 /* @todo check for overlaps and interact with user */ 173 } 174 175 synonymousNames.broadcast(this.resultToArrayToken(synNames)); 176 } catch (RemoteException re) { 177 _debug("<EXCEPTION> There was an error executing a remote query. " 178 + re + ". </EXCEPTION>"); 179 this.errBuf 180 .append( 181 "The Taxonomic Object Service reported an error executing ") 182 .append( 183 "the query. Please report the following information to ") 184 .append( 185 "the TOS administrators (astewart@ku.edu or rgales@ku.edu) ") 186 .append("\nAuthority: ").append(auth).append( 187 "\nRoot Name: ").append(name).append( 188 "\nTarget Level: ").append(level).append( 189 "\nCheck Overlaps: ").append(checkOverlaps) 190 .append("\n").append(re.toString()).append("\n\n"); 191 } catch (ServiceException se) { 192 _debug("<EXCEPTION> There was an error contacting the TOS. " + se 193 + ". </EXCEPTION>"); 194 this.errBuf 195 .append( 196 "The Taxonomic Object Service appears to be unavailable. ") 197 .append("Please report this to the TOS administrators ") 198 .append("(astewart@ku.edu or rgales@ku.edu).\n\n"); 199 } catch (MalformedURLException mue) { 200 _debug("<EXCEPTION> The URL for the TOS is invalid. " + mue 201 + ". </EXCEPTION>"); 202 } finally { 203 clientExecErrors.broadcast(new StringToken(this.errBuf.toString())); 204 } 205 } 206 207 /** @todo document me */ 208 private String[] callGetAuthorityNames() throws RemoteException, 209 ServiceException, MalformedURLException { 210 _debug("Calling getAuthortiyNames."); 211 CSOAPClient client = new CSOAPClient(URL); 212 return client.getAuthorityNames(); 213 } 214 215 /** @todo document me */ 216 private String[] callGetBestConcept(String name, String auth) 217 throws RemoteException, ServiceException, MalformedURLException { 218 _debug("Calling getBestConcept with name='" + name 219 + "' and authority='" + auth + "'."); 220 CSOAPClient client = new CSOAPClient(URL); 221 StringArrayHolder guidHolder = new StringArrayHolder(); 222 FloatArrayHolder wgtHolder = new FloatArrayHolder(); 223 client.getBestConcept(name, auth, guidHolder, wgtHolder); 224 return guidHolder.value; 225 } 226 227 /** @todo document me */ 228 private String[][] callGetSynsForAuthoritativeList(String guid, 229 String auth, String level) throws RemoteException, 230 ServiceException, MalformedURLException { 231 _debug("Calling getSynsForAuthoritativeList with guid='" + guid 232 + "' authority='" + auth + "' level='" + level + "'."); 233 CSOAPClient client = new CSOAPClient(URL); 234 return client.getSynsOfAuthoritativeList(guid, auth, level); 235 } 236 237 /** @todo document me */ 238 private ArrayToken resultToArrayToken(String[][] result) 239 throws IllegalActionException { 240 StringBuffer buf = new StringBuffer("{"); 241 242 for (int i = 0; i < result.length; i++) { 243 buf.append("{"); 244 for (int j = 0; j < result[i].length; j++) { 245 buf.append("\"").append(result[i][j]).append("\""); 246 247 if (j < (result[i].length - 1)) { 248 buf.append(", "); 249 } 250 } 251 buf.append("}"); 252 253 if (i < (result.length - 1)) { 254 buf.append(", "); 255 } 256 } 257 258 buf.append("}"); 259 260 return new ArrayToken(buf.toString()); 261 } 262}