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}