001/*
002 * Copyright (c) 2004-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.kepler.dataproxy.metadata.ADN;
031
032import java.io.IOException;
033import java.net.MalformedURLException;
034import java.net.URL;
035import java.util.Collections;
036import java.util.HashMap;
037import java.util.Hashtable;
038import java.util.Map;
039import java.util.Vector;
040
041import org.apache.commons.logging.Log;
042import org.apache.commons.logging.LogFactory;
043import org.ecoinformatics.ecogrid.queryservice.query.QueryType;
044import org.ecoinformatics.ecogrid.queryservice.resultset.ResultsetType;
045import org.ecoinformatics.ecogrid.queryservice.resultset.ResultsetTypeRecord;
046import org.ecoinformatics.ecogrid.queryservice.resultset.ResultsetTypeRecordReturnField;
047import org.ecoinformatics.ecogrid.queryservice.resultset.ResultsetTypeResultsetMetadata;
048import org.ecoinformatics.ecogrid.queryservice.resultset.ResultsetTypeResultsetMetadataRecordStructure;
049import org.ecoinformatics.ecogrid.queryservice.resultset.ResultsetTypeResultsetMetadataRecordStructureReturnField;
050import org.ecoinformatics.seek.ecogrid.MetadataSpecificationInterface;
051import org.ecoinformatics.seek.ecogrid.exception.EcoGridException;
052import org.ecoinformatics.seek.ecogrid.exception.InvalidEcogridQueryException;
053import org.ecoinformatics.seek.ecogrid.quicksearch.SearchQueryGenerator;
054import org.ecoinformatics.seek.ecogrid.quicksearch.SortableResultRecord;
055import org.ecoinformatics.seek.ecogrid.quicksearch.SortableResultRecordComparator;
056import org.kepler.configuration.ConfigurationManager;
057import org.kepler.configuration.ConfigurationProperty;
058import org.kepler.dataproxy.datasource.geon.GEONDatabaseResource;
059import org.kepler.dataproxy.datasource.geon.GEONShpResource;
060import org.xml.sax.SAXException;
061
062import ptolemy.actor.TypedAtomicActor;
063import ptolemy.kernel.CompositeEntity;
064import ptolemy.kernel.util.IllegalActionException;
065import ptolemy.kernel.util.NameDuplicationException;
066
067/**
068 * This class is a plugin for adn namespace to transfer query group to ecogrid
069 * query type. It also provide a method to create quick search query
070 * 
071 * @author Efrat Jaeger-Frank
072 */
073
074public class ADNMetadataSpecification extends MetadataSpecificationInterface {
075        public static final String ADNNAMESPACE = "http://www.sdsc.geongrid.org/services/search";
076        private static final int NAMESPACEARRAYLENGTH = 1;
077        private static final String OPERATOR = "LIKE";
078        private static final String FILERFIELD = "filerField";
079        private static final String UNKNOWNTITLE = "unknownTitle";
080        private static final String REPLACE = "#value#";
081        private static final String QUERYID = "geon-quick-search-query";
082        private static final int GAP = 106;
083
084        private int _numResults = 0;
085        protected String namespace = null;
086        protected String queryId = null;
087
088        private static final String DOCURL = "//documentation[namespace=\""
089                        + ADNNAMESPACE + "\"]/url";
090        private static final String DOCUSERNAME = "//documentation[namespace=\""
091                        + ADNNAMESPACE + "\"]/username";
092        private Map fieldIdtoNameMap = new HashMap();
093
094        // private String recordTitle = null;
095        // private Vector entityName = null;
096
097        protected final static Log log;
098        static {
099                log = LogFactory
100                                .getLog("org.kepler.dataproxy.metadata.ADN.ADNMetadataSpecification");
101        }
102
103        /**
104         * Default constructor
105         */
106        public ADNMetadataSpecification() {
107                namespace = ADNNAMESPACE; // TODO: REPLACE
108                queryId = QUERYID;
109        }// Eml200EcoGridQueryTransfer
110
111        /**
112         * Returns a URL to the ADN documentation
113         * 
114         * @throws MalformedURLException
115         */
116
117        public static URL getDocumentation(String recordId)
118                        throws MalformedURLException {
119        
120    ConfigurationManager confMan = ConfigurationManager.getInstance();
121    ConfigurationProperty documentationProperty = confMan.getProperty(
122      ConfigurationManager.getModule("common"));
123    String docURL =  documentationProperty.getProperty("documentation.url").getValue();
124    String userName = documentationProperty.getProperty("documentation.username").getValue();
125    
126                docURL += "?id=" + recordId + "&username=" + userName + "#in_browser";
127                return new URL(docURL);
128        }
129
130        /**
131         * Method to create a quick query search
132         * 
133         * @param value
134         *            String
135         * @return QueryType
136         */
137        public QueryType getQuickSearchEcoGridQuery(String value)
138                        throws InvalidEcogridQueryException {
139                Hashtable replaceMapping = new Hashtable();
140                replaceMapping.put(REPLACE, value);
141                SearchQueryGenerator eml200QueryGenerator = new SearchQueryGenerator(
142                                queryId, replaceMapping);
143                QueryType ecogridQuery = eml200QueryGenerator.getQuery();
144                return ecogridQuery;
145
146        }// getQuickSearchEcoGridQuery
147
148        /**
149         * Method to create a ecogrid query
150         * 
151         * @return QueryType
152         */
153        public QueryType getEcoGridQuery() {
154                QueryType ecogridQuery = null;
155                return ecogridQuery;
156        }// getEcoGridQuery
157
158        /**
159         * This method will transfer ResultsetType java object to array of
160         * ResultRecord java object. The ResultRecord object can be shown in kepler.
161         * If the results is null or there is no record in the result, null will be
162         * return
163         * 
164         * @param ResultsetType
165         *            results the result need to be transform
166         * @param String
167         *            endpoints the search end point
168         * @return ResultRecord[] the resultrecord need be returned.
169         */
170        public boolean transformResultset(ResultsetType results, String endpoint,
171                        CompositeEntity container, Vector aResultList) throws SAXException,
172                        IOException, EcoGridException, NameDuplicationException,
173                        IllegalActionException {
174                if (results == null) {
175                        return false; // ???
176                }
177
178                ResultsetTypeResultsetMetadata metaData = results
179                                .getResultsetMetadata();
180                if (metaData != null) {
181                        populateFieldMap(metaData);
182                }
183
184                // transfer ResultType to a vector of sorted titles containing
185                // (title,ids,returnFieldsVector)
186                Vector resultsetItemList = transformResultsetType(results);
187
188                // transfer the sored vector (contains eml2resultsetitem object to an
189                // array
190                // of ResultRecord
191                int numResults = resultsetItemList.size();
192
193                aResultList = new Vector();
194
195                Hashtable titleList = new Hashtable();// This hashtable is for keeping
196                                                                                                // track
197                // if there is a duplicate title
198                for (int i = 0; i < numResults; i++) {
199                        try {
200                                SortableResultRecord source = (SortableResultRecord) resultsetItemList
201                                                .elementAt(i);
202                                String title = source.getTitle();
203                                log.debug("The title is " + title);
204                                String id = source.getId();
205                                log.debug("The id is " + id);
206                                Vector returnFieldList = source.getEntityList();
207                                // if couldn't find id, skip this record
208                                if (id == null || id.trim().equals("")) {
209                                        continue;
210                                }
211
212                                // if couldn't find title, assign a one to it -- <j>
213                                if (title == null || title.trim().equals("")) {
214                                        title = "<" + i + ">";
215                                }
216                                if (titleList.containsKey(title)) {
217                                        title = title + " " + i;
218                                }
219                                titleList.put(title, title);
220
221                                String format = null;
222                                String description = "";
223
224                                for (int j = 0; j < returnFieldList.size(); j++) {
225                                        ResultsetTypeRecordReturnField returnField = (ResultsetTypeRecordReturnField) returnFieldList
226                                                        .elementAt(j);
227                                        if (returnField == null) {
228                                                continue;
229                                        }
230                                        String returnFieldId = returnField.getId();
231                                        String returnFieldValue = returnField.get_value();
232                                        String returnFieldName = (String) fieldIdtoNameMap
233                                                        .get(returnFieldId);
234                                        if (returnFieldName != null
235                                                        && !returnFieldName.trim().equals("")) {
236                                                if (returnFieldName.equals("description")) {
237                                                        log.debug("The description after parsing is  "
238                                                                        + returnFieldValue);
239                                                        description = returnFieldValue;
240                                                } else if (returnFieldName.equals("format")) {
241                                                        format = returnFieldValue;
242                                                }
243                                        }
244                                }
245
246                                TypedAtomicActor newRecord = null;
247                                if (format.trim().toLowerCase().indexOf("database") > -1) { // VERIFY!!!!
248                                        log.debug("The entiy is a database resource");
249                                        newRecord = new GEONDatabaseResource(container, title);
250                                        ((GEONDatabaseResource) newRecord)._idAtt.setExpression(id);
251                                        ((GEONDatabaseResource) newRecord)._endpointAtt
252                                                        .setExpression(endpoint);
253                                        ((GEONDatabaseResource) newRecord)._namespaceAtt
254                                                        .setExpression(namespace);
255                                        ((GEONDatabaseResource) newRecord)._descriptionAtt
256                                                        .setExpression(restyleDescription(description));
257                                } else if (format.equals("shapefile")) {
258                                        log.debug("The entiy is a shapefile resource");
259                                        newRecord = new GEONShpResource(container, title);
260                                        ((GEONShpResource) newRecord)._idAtt.setExpression(id);
261                                        ((GEONShpResource) newRecord)._endpointAtt
262                                                        .setExpression(endpoint);
263                                        ((GEONShpResource) newRecord)._namespaceAtt
264                                                        .setExpression(namespace);
265                                        ((GEONShpResource) newRecord)._descriptionAtt
266                                                        .setExpression(restyleDescription(description));
267
268                                } else
269                                        continue;
270
271                                aResultList.add(newRecord);
272
273                        } catch (Exception e) {
274                                continue;
275                        }
276                }// for
277
278                _numResults = aResultList.size();
279                return _numResults > 0;
280        }
281
282        /*
283         * Method to transform array of AnyRecordType to array of EML2ResultsetItem
284         */
285        private Vector transformResultsetType(ResultsetType result) {
286                Vector itemList = new Vector();
287                if (result == null) {
288                        return itemList;
289                }
290                ResultsetTypeRecord[] records = result.getRecord();
291                if (records == null) {
292                        return itemList;
293                }
294
295                int numRecords = records.length;
296                // transfer every records from source to a dest - EML2ResultsetItem obj
297                for (int i = 0; i < numRecords; i++) {
298                        ResultsetTypeRecord currentRecord = records[i];
299                        if (currentRecord == null) {
300                                continue;
301                        }
302                        String docid = currentRecord.getIdentifier();
303                        log.debug("The doc id after parsing resultset is " + docid);
304                        // if couldn't find identifier, we don't need it
305                        if (docid == null || docid.trim().equals("")) {
306                                continue;
307                        }
308                        ResultsetTypeRecordReturnField[] recordReturnFieldList = currentRecord
309                                        .getReturnField();
310                        if (recordReturnFieldList == null) {
311                                continue;
312                        }
313
314                        String recordTitle = null;
315
316                        Vector returnFieldList = new Vector();
317
318                        int length = recordReturnFieldList.length;
319                        for (int j = 0; j < length; j++) {
320                                ResultsetTypeRecordReturnField currentReturnField = recordReturnFieldList[j];
321                                if (currentReturnField == null) {
322                                        continue;
323                                }
324                                String returnFieldId = currentReturnField.getId();
325                                String returnFieldValue = currentReturnField.get_value();
326                                String returnFieldName = (String) fieldIdtoNameMap
327                                                .get(returnFieldId);
328                                if (returnFieldName != null
329                                                && !returnFieldName.trim().equals("")) {
330                                        if (returnFieldName.equals("title")) {
331                                                log.debug("The title after parsing is  "
332                                                                + returnFieldValue);
333                                                recordTitle = returnFieldValue;
334                                                recordTitle = replaceDotByDash(recordTitle);
335                                        } else {
336                                                returnFieldList.add(currentReturnField); // the other
337                                                                                                                                        // field
338                                                                                                                                        // will be
339                                                                                                                                        // used
340                                                                                                                                        // later.
341                                        }
342                                }
343
344                        }// for
345                        if (recordTitle == null) {
346                                recordTitle = UNKNOWNTITLE + i;
347                        }
348                        SortableResultRecord newItem = new SortableResultRecord(
349                                        recordTitle, docid, returnFieldList);
350                        itemList.add(newItem);
351
352                }// for
353                Collections.sort(itemList, new SortableResultRecordComparator());
354                return itemList;
355        }
356
357        /**
358         * Initialize the mapping between return fields ids and names
359         * 
360         * @param metaData
361         */
362        private void populateFieldMap(ResultsetTypeResultsetMetadata metaData) {
363                ResultsetTypeResultsetMetadataRecordStructure recordStructure = metaData
364                                .getRecordStructure();
365                ResultsetTypeResultsetMetadataRecordStructureReturnField[] fields = recordStructure
366                                .getReturnField();
367                if (fields != null) {
368                        for (int i = 0; i < fields.length; i++) {
369                                String value = fields[i].getName();
370                                String id = fields[i].getId();
371                                fieldIdtoNameMap.put(id, value);
372                                // fieldNametoIdHash.put( value, id );
373                        }
374                }
375        }
376
377        /**
378         * Add new lines to provider a nicer display of the description.
379         * 
380         * @param detail
381         *            the additional information about the record
382         */
383        private String restyleDescription(String description) {
384                int fromInd = 0;
385                int prevSpaceInd = 0;
386                String newDescription = "";
387                int descLen = description.length();
388                while (fromInd + GAP < descLen) {
389                        int nextSpaceInd = description.indexOf(' ', fromInd + GAP);
390                        prevSpaceInd = description.lastIndexOf(' ', nextSpaceInd - 1); // make
391                                                                                                                                                        // it
392                                                                                                                                                        // backword.
393                        newDescription += description.substring(fromInd, prevSpaceInd)
394                                        + "\n";
395                        fromInd = prevSpaceInd + 1;
396                }
397                newDescription += description.substring(fromInd);
398                return newDescription;
399        }
400
401        /**
402         * Creates GEONDataResource items (derived from Source) and adds them into
403         * the container
404         * 
405         * @param aResults
406         * @param aEndPointURLStr
407         * @param aContainer
408         * @param aResultList
409         * @throws SAXException
410         * @throws IOException
411         * @throws EcoGridException
412         * @throws NameDuplicationException
413         * @throws IllegalActionException
414         */
415        public boolean addResultsetRecordsToContainer(ResultsetType results,
416                        String endpoint, CompositeEntity container, Vector aResultList)
417                        throws SAXException, IOException, EcoGridException,
418                        NameDuplicationException, IllegalActionException {
419                // parse the resultset into ResultRecord array and stored it into
420                // a vector
421                try {
422                        return transformResultset(results, endpoint, container, aResultList);
423                } catch (Exception ee) {
424                        log
425                                        .debug(
426                                                        "The error to transform from resultset to GEONDataResource ",
427                                                        ee);
428                }
429                return false;
430        }
431
432        /**
433         * 
434         * @return Returns a unique name that descrobes this class, often it is the
435         *         name of the class that implments the interface
436         */
437        public String getName() {
438                return getClass().getName();
439        }
440
441        /**
442     * 
443     */
444        public String getBriefName() {
445                return "Geology";
446        }
447
448        /**
449         * 
450         * @return returns the number of results that for this data.
451         */
452        public int getNumResults() {
453                return _numResults;
454        }
455
456}// Eml200EcoGridQueryTransfer