org.ecoinformatics.seek.datasource.eml.eml2.Eml200DataSource.java Source code

Java tutorial

Introduction

Here is the source code for org.ecoinformatics.seek.datasource.eml.eml2.Eml200DataSource.java

Source

/*
 * Copyright (c) 2003-2010 The Regents of the University of California.
 * All rights reserved.
 *
 * '$Author: jones $'
 * '$Date: 2014-02-06 15:25:50 -0800 (Thu, 06 Feb 2014) $' 
 * '$Revision: 32582 $'
 * 
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the above
 * copyright notice and the following two paragraphs appear in all copies
 * of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
 * ENHANCEMENTS, OR MODIFICATIONS.
 *
 */

package org.ecoinformatics.seek.datasource.eml.eml2;

import java.awt.BorderLayout;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.TableModel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ecoinformatics.ecogrid.client.IdentifierServiceClient;
import org.ecoinformatics.seek.dataquery.DBTableNameResolver;
import org.ecoinformatics.seek.dataquery.DBTablesGenerator;
import org.ecoinformatics.seek.dataquery.HsqlDataQueryAction;
import org.ecoinformatics.seek.datasource.DataSourceIcon;
import org.ecoinformatics.seek.datasource.EcogridDataCacheItem;
import org.ecoinformatics.seek.datasource.EcogridGZippedDataCacheItem;
import org.ecoinformatics.seek.datasource.EcogridMetaDataCacheItem;
import org.ecoinformatics.seek.datasource.EcogridTarArchivedDataCacheItem;
import org.ecoinformatics.seek.datasource.EcogridZippedDataCacheItem;
import org.ecoinformatics.seek.ecogrid.EcoGridService;
import org.ecoinformatics.seek.ecogrid.EcoGridServicesController;
import org.ecoinformatics.seek.ecogrid.quicksearch.ResultRecord;
import org.ecoinformatics.seek.ecogrid.quicksearch.ResultTreeRoot;
import org.ecoinformatics.seek.querybuilder.DBQueryDef;
import org.ecoinformatics.seek.querybuilder.DBQueryDefParserEmitter;
import org.ecoinformatics.seek.querybuilder.DBSchemaParserEmitter;
import org.kepler.actor.preview.Previewable;
import org.kepler.objectmanager.ActorMetadata;
import org.kepler.objectmanager.cache.ActorCacheObject;
import org.kepler.objectmanager.cache.CacheManager;
import org.kepler.objectmanager.cache.DataCacheListener;
import org.kepler.objectmanager.cache.DataCacheManager;
import org.kepler.objectmanager.cache.DataCacheObject;
import org.kepler.objectmanager.data.DataSourceControllerFactory;
import org.kepler.objectmanager.data.DataType;
import org.kepler.objectmanager.data.db.DSSchemaDef;
import org.kepler.objectmanager.data.db.DSSchemaIFace;
import org.kepler.objectmanager.data.db.DSTableFieldIFace;
import org.kepler.objectmanager.data.db.Entity;
import org.kepler.objectmanager.data.db.QBTableauFactory;
import org.kepler.objectmanager.data.text.TextComplexFormatDataReader;
import org.kepler.objectmanager.lsid.KeplerLSID;
import org.kepler.util.DelimitedReader;
import org.kepler.util.DotKeplerManager;
import org.xml.sax.InputSource;

import ptolemy.actor.TypedIOPort;
import ptolemy.actor.gui.style.TextStyle;
import ptolemy.data.BooleanToken;
import ptolemy.data.DoubleToken;
import ptolemy.data.IntToken;
import ptolemy.data.LongToken;
import ptolemy.data.StringToken;
import ptolemy.data.Token;
import ptolemy.data.expr.FileParameter;
import ptolemy.data.expr.Parameter;
import ptolemy.data.expr.StringParameter;
import ptolemy.data.type.BaseType;
import ptolemy.data.type.Type;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.ChangeRequest;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.InternalErrorException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.NamedObj;
import ptolemy.kernel.util.Settable;
import ptolemy.kernel.util.StringAttribute;
import ptolemy.moml.MoMLChangeRequest;
import ptolemy.util.CancelException;
import ptolemy.util.MessageHandler;
import ptolemy.vergil.basic.KeplerDocumentationAttribute;
import util.PersistentTableModel;
import util.PersistentTableModelWindowListener;
import util.PersistentVector;
import util.StaticUtil;
import util.TableSorter;
import EDU.oswego.cs.dl.util.concurrent.CountDown;
import EDU.oswego.cs.dl.util.concurrent.Latch;

/**
 * <p>
 * The Eml200DataSource is used to gain access to a wide variety of data
 * packages that have been described using Ecological Metadata Language (EML).
 * Each data package contains an EML metadata description and one or more data
 * entities (data tables, spatial raster images, spatial vector images). The
 * data packages can be accessed from the local filesystem or through any
 * EcoGrid server that provides access to its collection of data objects.
 * </p>
 * <p>
 * The metadata provided by the EML description of the data allows the data to
 * be easily ingested into Kepler and exposed for use in downstream components.
 * The Eml200DataSource handles all of the mechanical issues associated with
 * parsing the metadata, downloading the data from remote servers if applicable,
 * understanding the logical structure of the data, and emitting the data for
 * downstream use when required. The supported data transfer protocols include
 * http, ftp, file, ecogrid and srb.
 * </p>
 * <p>
 * After parsing the EML metadata, the actor automatically reconfigures its
 * exposed ports to provide one port for each attribute in the first entity that
 * is described in the EML description. For example, if the first entity is a
 * data table with four columns, the ports might be "Site", "Date", "Plot", and
 * "Rainfall" if that's what the data set contained. These details are obtained
 * from the EML document.
 * </p>
 * <p>
 * By default, the ports created by the EML200DataSource represent fields in the
 * data entities, and one tuple of data is emitted on these ports during each
 * fire cycle. Alternatively, the actor can be configured to so that the ports
 * instead represent an array of values for a field ("AsColumnVector"), or so
 * that the ports represent an entire table of data ("AsTable") formatted in
 * comma-separated-value (CSV) format.
 * </p>
 * <p>
 * If more than one data entity is described in the EML metadata, then the
 * output of the actor defaults to the first entity listed in the EML. To select
 * the other entities, one must provide a query statement that describes the
 * filter and join that should be used to produce the data to be output. This is
 * accomplished by selecting 'Open actor', which shows the Query configuration
 * dialog, which can be used to select the columns to be output and any
 * filtering constraints to be applied.
 * </p>
 * 
 * @author Matt Jones, Jing Tao, Chad Berkley
 * @since kepler-1.0.0
 * @Pt.ProposedRating Red (jones)
 * @Pt.AcceptedRating Red (jones)
 */
public class Eml200DataSource extends ResultRecord implements DataCacheListener, Previewable {

    /*
     * Brief discussion of threads:
     * 
     * The actor startup will call attributeChanged multiple time to configure
     * the object. When the recordId and endpoint attributes are set, the object
     * will attempt to load the EcoGridMetaDataCacheItem. This operation will
     * fork a thread named "MetaData XYZZY".
     * 
     * The MetaData thread is configured to use the inner class MetadataComplete
     * listener when it completes. The thread, in this listener, initializes the
     * CountDown _entityCountDown and kicks off threads to load each entity. The
     * thread then waits using _entityCountDown.acquire() until all the data
     * entity thread are complete.
     * 
     * Each data entity is loaded using a class derived from
     * EcogridDataCacheItem. The specific type of class is determined from ...
     */

    // /////////////////////////////////////////////////////////////////
    // // private variables ////
    static final String DATATYPES[] = { DataType.INT, DataType.FLOAT, DataType.DOUBLE, DataType.LONG,
            DataType.STR };

    static final BaseType BASETYPES[] = { BaseType.INT, BaseType.DOUBLE, BaseType.DOUBLE, BaseType.LONG,
            BaseType.STRING };

    static private Hashtable _TypeHash = new Hashtable();

    // for looking using the default documentation from the actor lib
    private static KeplerDocumentationAttribute defaultDocumentation = null;

    static Log log;
    static {
        log = LogFactory.getLog("org.ecoinformatics.seek.datasource.eml.eml2.Eml200DataSource");
    }

    private QBTableauFactory _qbTableauFactory = null;

    private DataSourceControllerFactory _nodeController = null;

    /**
     * Output indicator parameter.
     */
    private Eml200DataOutputFormatBase _dataOutputFormat = null;

    private Vector<Entity> _entityList = new Vector<Entity>();
    private Entity _selectedTableEntity = null;

    private Vector _columns = null;

    private DBQueryDef _queryDef = null;

    private DSSchemaIFace _schemaDef = null;
    private DSSchemaDef _schema = new DSSchemaDef();

    private boolean _ignoreSchemaChange = false;

    private boolean _schemaDefinitionIsSet = false;

    private DBTablesGenerator _tableGenerator = null;

    private EcogridDataCacheItem _selectedCachedDataItem = null;

    private EcogridMetaDataCacheItem _cacheMetaDataItem = null;

    /**
     * _metadataCompleted is a Latch object (from oswego) which is used to
     * synchronize the start of the workflow. The Latch is released when the
     * _cacheMetaDataItem has been completed. It is also used to notify a thread
     * blocked in initialized() when the stop method is executed.
     */
    private Latch _metadataCompleted = new Latch();

    private InputStream _reader = null;

    private HsqlDataQueryAction _resultSet = null;

    /*
     * Indicates when there is no more data in the _resultSet to output.
     */
    private boolean _endOfResultSet = false;

    private DelimitedReader _simpleDelimitedReader = null;

    private TextComplexFormatDataReader _complexFormatReader = null;

    private CountDown _entityCountDown = null;
    private int _numberOfEntities = 0;

    private int _numberOfFailedDownloadEntities = 0;

    private boolean _hasSQLCommand = false;

    private String[] _selectedColumnLabelList = null;

    private Type[] _selectedColumnTypeList = null;

    private static final int INDEXFORNOTFOUND = -1;

    private static final int DEFAULTINDEX = 0;

    private DataSourceIcon _icon;

    private String emlFile = null;

    private String emlFileFinalPath = null;

    private Vector failedDownloadEntityName = new Vector();

    /**
     * Tracks if user has been asked about newer version of eml before (Kind of
     * a hack to get around mysterious multiple attributeChanged() calls)
     */
    private int checkVersionPromptCount = 0;

    /**
     * The default endpoint for EcoGrid messages, which is overridden in the
     * configuration file.
     */
    private static final String ENDPOINT = "http://ecogrid.ecoinformatics.org/knb/services/QueryService";

    private static final String HTMLEXTENSION = ".html";

    public static final Settable.Visibility DEFAULTFORSQL = Settable.EXPERT;

    public static final Settable.Visibility DEFAULTFORSCHEMA = Settable.EXPERT;

    private static final boolean SHOWGUIERROR = true;

    // private boolean isDoneParseEmlFile = false;

    // /////////////////////////////////////////////////////////////////
    // // ports and parameters ////

    /**
     * The file path for locating an EML file that is available from a local
     * file.
     */
    public FileParameter emlFilePath = null;

    /**
     * The file path for locating a data file that is available from the local
     * file system
     */
    public FileParameter dataFilePath = null;

    /**
     * The SQL command which will be applied to the data entity to filter data
     * values. This is usually generated using the Query configuration dialog.
     */
    public StringAttribute sqlDef = null;

    /**
     * Schema definition for the entities in this package. The schema definition
     * is obtained automatically by parsing the EMl document and does not need
     * to be edited by the end user.
     */
    public StringAttribute schemaDef = null;

    /**
     * The format of the output to be produced for the data entity. This
     * parameter controls which ports are created for the actor and what data is
     * emitted on those ports during each fire cycle. For example, this field
     * can be configured to produce one port for each column in a data table, or
     * one port that emits the entire data table at once in CSV format.
     * Specifically, the output format choices are:
     * <p>
     * As Field: This is the default. One output port is created for each field
     * (aka column/attribute/variable) that is described in the EML metadata for
     * the data entity. If the SQL statement has been used to subset the data,
     * then only those fields selected in the SQL statement will be configured
     * as ports.
     * </p>
     * <p>
     * As Table: The selected entity will be sent out as a string which contains
     * the entire entity data. It has three output ports: DataTable - the data
     * itself, Delimiter - delimiter to seperate fields, and NumColumns - the
     * number of fields in the table.
     * </p>
     * <p>
     * As Row: In this output format, one tuple of selected data is formatted as
     * an array and sent out. It only has one output port (DataRow) and the data
     * type is a record containing each of the individuals fields.
     * </p>
     * <p>
     * As Byte Array: Selected data will be sent out as an array of bytes which
     * are read from the data file. This is the raw data being sent in binary
     * format. It has two output ports: BinaryData - contains data itself, and
     * EndOfStream - a tag to indicate if it is end of data stream.
     * </p>
     * <p>
     * As UnCompressed File Name: This format is only used when the entity is a
     * compressed file (zip, tar et al). The compressed archive file is
     * uncompressed after it is downloaded. It has only one output port which
     * will contain an array of the filenames of all of the uncompressed files
     * from the archive. If the parameter "Target File Extension in Compressed
     * File" is provided, then instead the array that is returned will only
     * contain the files with the file extension provided.
     * </p>
     * <p>
     * As Cache File Name: Kepler stores downloaded data files from remote sites
     * into its cache system. This output format will send the local cache file
     * path for the entity so that workflow designers can directly access the
     * cache files. It has two output ports: CacheLocalFileName - the local file
     * path, and CacheResourceName - the data link in eml for this enity.
     * </p>
     * <p>
     * As Column Vector: This output format is similar to "As Field". The
     * difference is instead sending out a single value on each port, it sends
     * out an array of all of the data for that field. The type of each port is
     * an array of the base type for the field.
     * </p>
     * <p>
     * As ColumnBased Record: This output format will send all data on one port
     * using a Record structure that encapsulates the entire data object. The
     * Record will contain one array for each of the fields in the data, and the
     * type of that array will be determined by the type of the field it
     * represents.
     * </p>
     */
    public StringParameter dataOutputFormat = null;

    /**
     * This parameter specifies a file extension that is used to limit the array
     * of filenames returned by the data source actor when "As unCompressed File
     * Name" is selected as the ouput type. Please see more information in
     * "As Uncompressed File Name" in the description of the output format
     * parameter.
     */
    public StringParameter fileExtensionFilter = null;

    /**
     * This parameter determines if extra data columns that are NOT described in
     * the EML should be ignored (isLenient=true) or if an error should be
     * raised when the data and EML description do not match (isLenient=false)
     * TRUE - extra data columns are ignored FALSE - an error is raised when
     * data and metadata conflict
     */
    public Parameter isLenient = null;

    /**
     * This parameter determines if remote source should be queried for latest
     * revision of metadata file. TRUE - check performed FALSE - do not check
     * for latest version
     */
    public Parameter checkVersion = null;

    /**
     * If this EML package has mutiple entities, this parameter specifies which
     * entity should be used for output. By default when this parameter is
     * unset, data from the first entity described in an EML package is output.
     * This parameter is only used if the SQL parameter is not used, or if the
     * SQL parameter is used and the output format is one of "As Table",
     * "As Byte Array", "As Uncompressed File Name", and "As Cache File Name".
     */
    public StringParameter selectedEntity = null;

    /**
     * Construct an actor with the given container and name.
     * 
     * @param container
     *            The container.
     * @param name
     *            The name of this actor.
     * @exception IllegalActionException
     *                If the actor cannot be contained by the proposed
     *                container.
     * @exception NameDuplicationException
     *                If the container already has an actor with this name.
     * @since
     */
    public Eml200DataSource(CompositeEntity container, String name)
            throws NameDuplicationException, IllegalActionException {
        super(container, name);
        _icon = new DataSourceIcon(this);

        emlFilePath = new FileParameter(this, "emlFilePath");
        emlFilePath.setDisplayName("EML File");

        dataFilePath = new FileParameter(this, "dataFilePath");
        dataFilePath.setDisplayName("Data File");

        schemaDef = new StringAttribute(this, "schemaDef");
        TextStyle schemaDefTS = new TextStyle(schemaDef, "schemaDef");
        schemaDef.setDisplayName("Schema Definition");
        schemaDef.setVisibility(DEFAULTFORSCHEMA);

        sqlDef = new StringAttribute(this, "sqlDef");
        TextStyle sqlDefTS = new TextStyle(sqlDef, "sqlDef");
        sqlDef.setDisplayName("SQL Command");
        sqlDef.setVisibility(DEFAULTFORSQL);
        selectedEntity = new StringParameter(this, "selectedEntity");
        selectedEntity.setDisplayName("Selected Entity");
        dataOutputFormat = new StringParameter(this, "dataOutputFormat");
        dataOutputFormat.setDisplayName("Data Output Format");
        dataOutputFormat.setExpression(Eml200DataOutputFormatFactory._AsField);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsField);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsTable);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsRow);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsByteArray);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsUnzippedFileName);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsFileName);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsAllFileNames);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsColumnVector);
        dataOutputFormat.addChoice(Eml200DataOutputFormatFactory._AsColumnRecord);
        _dataOutputFormat = Eml200DataOutputFormatFactory.newInstance(this);

        fileExtensionFilter = new StringParameter(this, "fileExtensionFilter");
        fileExtensionFilter.setDisplayName("File Extension Filter");

        isLenient = new Parameter(this, "isLenient");
        isLenient.setDisplayName("Allow lenient data parsing");
        isLenient.setTypeEquals(BaseType.BOOLEAN);
        isLenient.setToken(BooleanToken.FALSE);

        checkVersion = new Parameter(this, "checkVersion");
        checkVersion.setDisplayName("Check for latest version");
        checkVersion.setTypeEquals(BaseType.BOOLEAN);
        checkVersion.setToken(BooleanToken.FALSE);

        // create tableau for editting the SQL String
        _qbTableauFactory = new QBTableauFactory(this, "_tableauFactory");

        // Create a node controller to control the context menu
        _nodeController = new DataSourceControllerFactory(this, "_controllerFactory");

        if (_TypeHash.size() == 0) {
            for (int i = 0; i < DATATYPES.length; i++) {
                _TypeHash.put(DATATYPES[i], BASETYPES[i]);
            }
        }

    }

    /**
     * Accessor to _columns member. Default permissions for use by
     * Eml200DataOutputFormatBase derived classes only.
     * 
     *     */
    public Vector getColumns() {
        return _columns;
    }

    /**
     * @return Returns the _selectedTableEntity.
     */
    Entity getSelectedTableEntity() {
        return _selectedTableEntity;
    }

    public Vector<Entity> getEntityList() {
        return _entityList;
    }

    /**
     * @return Returns the _selectedCachedDataItem.
     */
    EcogridDataCacheItem getSelectedCachedDataItem() {
        return _selectedCachedDataItem;
    }

    /**
     * @return Returns the fileExtensionFilter.
     */
    String getFileExtensionInZip() {
        try {
            return fileExtensionFilter.stringValue();
        } catch (IllegalActionException e) {
            return "";
        }
    }

    /**
     * @return Returns the _selectedColumnLabelList.
     */
    String[] getColumnLabels() {
        return _selectedColumnLabelList;
    }

    /**
     * @return Returns the _selectedColumnTypeList.
     */
    Type[] getColumnTypes() {
        return _selectedColumnTypeList;
    }

    public void preinitialize() throws IllegalActionException {

        // check for latest
        this.checkForMostRecentRecordId(false);

        // First block for metadata download to finish.
        try {
            _metadataCompleted.acquire();
            log.debug("Is stop requested? " + getDirector().isStopRequested());
        } catch (InterruptedException e) {
            log.debug("Is stop requested? " + getDirector().isStopRequested());
            if (getDirector().isStopRequested()) {
                throw new IllegalActionException("Execution interrupted");
            }
        }

        super.preinitialize();
    }

    /**
     * Initialize the actor prior to running in the workflow. This reads the
     * metadata and configures the ports.
     * 
     * @throws IllegalActionException
     */
    public void initialize() throws IllegalActionException {
        log.debug("In initialize method");

        // Now block waiting for the entity data to finish.
        try {
            synchronized (_entityCountDown) {
                while (_entityCountDown.currentCount() > 0
                        && (getDirector() != null && !getDirector().isStopRequested())) {
                    _entityCountDown.wait();
                    log.debug("Is stop requested? " + getDirector().isStopRequested());
                }
            }
        } catch (InterruptedException e) {
            throw new IllegalActionException("Downloads not completed");
        } catch (Exception e) {
            throw new IllegalActionException("Download error encountered");
        }

        if (getDirector() != null && getDirector().isStopRequested()) {
            throw new IllegalActionException("Execution interrupted");
        }

        if (_selectedTableEntity == null) {
            throw new IllegalActionException("_selectedTableEnity is NULL!");
        }

        if (_selectedCachedDataItem == null) {
            _selectedCachedDataItem = (EcogridDataCacheItem) _selectedTableEntity.getDataCacheObject();
        }

        if (_selectedCachedDataItem == null) {
            throw new IllegalActionException(" The selected entity has a null data (Maybe data download failed)");
        }

        // This was the initializeAsTableRowOrField method
        String sqlStr = "";
        String sqlXMLStr = ((Settable) sqlDef).getExpression();
        if (sqlXMLStr == null || sqlXMLStr.length() == 0) {
            sqlStr = "SELECT * FROM "
                    + (_selectedTableEntity.getMappedName() != null ? _selectedTableEntity.getMappedName()
                            : _selectedTableEntity.getName());

        } else {
            Hashtable mappedNameHash = new Hashtable();
            // should go through all enities
            int size = _entityList.size();
            if (size == 0) {
                // no entity in this package and throw an exception
                throw new IllegalActionException(
                        "There is no downloadable entity or no entity in this eml package");
            }
            for (int i = 0; i < size; i++) {
                Entity entity = (Entity) _entityList.elementAt(i);
                if (entity.getMappedName() != null) {
                    mappedNameHash.put(entity.getName(), entity.getMappedName());
                }
            }
            DBQueryDef queryDef = DBQueryDefParserEmitter.parseQueryDef(_schemaDef, sqlXMLStr, mappedNameHash);

            sqlStr = DBQueryDefParserEmitter.createSQL(_schemaDef, queryDef);
        }

        log.debug("The sql command is " + sqlStr);
        // excuted query
        if (sqlStr != null && !sqlStr.trim().equals("")) {
            // if table gnerated successfully, we will run query
            if (_tableGenerator != null && _tableGenerator.getSuccessStatus()) {
                try {
                    _icon.setBusy();
                    _resultSet = new HsqlDataQueryAction();
                    _resultSet.setSQL(sqlStr);
                    _resultSet.actionPerformed(null);

                } catch (Exception e) {
                    log.debug("Error to run query is ", e);
                    throw new IllegalActionException(e.getMessage());
                }

            }
        }
        // if reustlset is null(this can be caused by db couldn't create table)
        // and we don't have any sql command
        // (this means we only have one data entity involve) or only has one
        // entity at all
        // we would like to try read the selected entity data from datachache
        // rather than
        // from db
        if (_resultSet == null && (!_hasSQLCommand || _numberOfEntities == 1)) {
            // System.out.println("in result set is null!!!!!!!!!!!!!!!!!!");
            _reader = _selectedCachedDataItem.getDataInputStream();
            try {
                createDelimitedReader();
            } catch (Exception e) {
                log.debug("Error to run delimiter reader is  ", e);
                throw new IllegalActionException(e.getMessage());
            }

        }
        _icon.setReady();
        // This was the initializeAsTableRowOrField method

        // Set marker to say we have data. This might not be true though and is
        // perhaps
        // a bug. The correct thing to do, is check for data in the prefire and
        // in the
        // postfire.
        _endOfResultSet = false;

        _dataOutputFormat.initialize();

    }

    /**
     * This method will read a row vector from data source, either from
     * resultset which excuted by data query or delimiterdReader which reader
     * from data inputtream - _reader. This method will be called in asFired
     * method
     */
    public Vector gotRowVectorFromSource() throws Exception {
        Vector rowVector = new Vector();
        if (_resultSet != null) {

            ResultSet rs = _resultSet.getResultSet();
            ResultSetMetaData metadata = rs.getMetaData();
            int columnSize = metadata.getColumnCount();

            if (rs.next()) {
                for (int i = 0; i < columnSize; i++) {
                    String str = rs.getString(i + 1);
                    rowVector.add(str);
                }
            }
        } else if (_reader != null && (!_hasSQLCommand || _numberOfEntities == 1)) {
            if (_selectedTableEntity.isSimpleDelimited()) {
                _simpleDelimitedReader.setCollapseDelimiter(_selectedTableEntity.getCollapseDelimiter());
                _simpleDelimitedReader.setNumFooterLines(_selectedTableEntity.getNumFooterLines());
                rowVector = _simpleDelimitedReader.getRowDataVectorFromStream();
            } else {
                rowVector = _complexFormatReader.getRowDataVectorFromStream();
            }

        }

        if (rowVector.isEmpty()) {
            _endOfResultSet = true;
        }
        return rowVector;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ptolemy.actor.lib.Source#prefire()
     */
    public boolean prefire() throws IllegalActionException {
        return _dataOutputFormat.prefire();
    }

    /**
     * Send a record's tokens over the ports on each fire event.
     * 
     * @exception IllegalActionException
     *                If there is no director.
     */
    public void fire() throws IllegalActionException {
        // log.debug("In fire method");
        super.fire();
        _dataOutputFormat.fire();
    }

    /**
     * This method is only for output as byte array. Read the next bytes from
     * the input stream into array. Fire method will send the array out. If
     * there reached EOF , return false. Otherwise, return whatever the
     * superclass returns.
     * 
     * @exception IllegalActionException
     *                If there is a problem reading the file.
     */
    public boolean postfire() throws IllegalActionException {

        if (!_dataOutputFormat.postfire()) {
            return false;
        }

        try {
            if (_resultSet != null && _resultSet.getResultSet().isAfterLast()) {
                return false;
            }
        } catch (SQLException e) {
            throw new IllegalActionException(this, e, "Unable to determine end of result set");
        }

        if (_endOfResultSet) {
            return false;
        }

        return super.postfire();

    }

    void initializePort(String aPortName, Type aPortType) throws IllegalActionException {
        try {
            String columnName = aPortName.trim();
            // Create a new port for each Column in the resultset
            TypedIOPort port = (TypedIOPort) this.getPort(columnName);
            boolean aIsNew = (port == null);
            if (aIsNew) {
                // Create a new typed port and add it to this container
                port = new TypedIOPort(this, columnName, false, true);
                log.debug("Creating port [" + columnName + "]" + this);
            }
            port.setTypeEquals(aPortType);

        } catch (ptolemy.kernel.util.NameDuplicationException nde) {
            throw new IllegalActionException(
                    "One or more attributes has the same name.  Please correct this and try again.");
        }

    }

    /*
     * Remove all ports which's name is not in the selected vector
     */
    void removeOtherOutputPorts(Collection nonRemovePortName) throws IllegalActionException {
        // Use toArray() to make a deep copy of this.portList().
        // Do this to prevent ConcurrentModificationExceptions.
        TypedIOPort[] l = new TypedIOPort[0];
        l = (TypedIOPort[]) this.portList().toArray(l);

        for (int i = 0; i < l.length; i++) {
            TypedIOPort port = l[i];
            if (port == null || port.isInput()) {
                continue;
            }
            String currPortName = port.getName();
            if (!nonRemovePortName.contains(currPortName)) {
                try {
                    port.setContainer(null);
                } catch (Exception ex) {
                    throw new IllegalActionException(this, "Error removing port: " + currPortName);
                }
            }
        }
    }

    /**
     * Issue a ChangeRequest to change the output ports.
     * 
     * @throws ptolemy.kernel.util.IllegalActionException
     */
    private void reconfigurePorts(String why) {
        log.debug("Creating reconfigure ports change request " + why);
        this.requestChange(new ChangeRequest(this, why) {
            public void _execute() throws Exception {
                log.debug("Executing reconfigure ports change request " + this.getDescription());
                _dataOutputFormat.reconfigurePorts();
            }
        });
    }

    /**
     * Callback for changes in attribute values.
     */
    public void attributeChanged(ptolemy.kernel.util.Attribute attribute)
            throws ptolemy.kernel.util.IllegalActionException {
        log.debug("In attribute change method");
        // System.out.println("In attribute change method!!!!!!!!!!!!!!!");
        if (attribute == emlFilePath || attribute == dataFilePath) {
            log.debug("Processing new EML or data file path...");
            try {
                String url = ((Settable) emlFilePath).getExpression();
                log.debug("EML File Path is: " + emlFilePath);

                String dataurl = dataFilePath.getExpression();
                if (dataurl == null || dataurl.trim().equals("")) {
                    log.debug("Data file is null so returning...");
                    return;
                }

                if (url == null || url.trim().equals("")) {
                    log.debug("URL is null so returning...");
                    return;
                }

                if (emlFile != null && emlFile.equals(url)) {
                    log.debug("EML File is null so returning...");
                    return;
                }
                emlFile = url;
                String endpoint = null;
                if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("file:///")
                        && !url.startsWith("ftp://") && !url.startsWith("ecogrid://")
                        && !url.startsWith("srb://")) {
                    log.debug("In url mangling block");
                    if (emlFilePath == null) {
                        return;
                    }
                    File emlFileName = emlFilePath.asFile();
                    //System.out.println("the name of eml file name" +emlFileName.getName());
                    setRecordId(emlFileName.getName());
                    if (emlFileName == null) {
                        return;
                    }
                    url = emlFileName.getPath();
                    // System.out.println("the url from getpath is "+url);
                    // it is file path we need add file protocal
                    if (url.startsWith("file:/") && !url.startsWith("file://") && !url.startsWith("file:///")) {

                        // somehow windows url will look like "file:/C:
                        url = url.replaceFirst("file:/", "file:///");

                    } else if (url.startsWith("file://") && !url.startsWith("file:///")) {

                        url = url.replaceFirst("file://", "file:///");
                    } else if (!url.startsWith("file:///")) {
                        // it is file path we need add file protocal
                        url = "file:///" + url;
                    }
                }
                emlFileFinalPath = url;
                log.debug("Final EML url is: " + emlFileFinalPath);

                _icon.setBusy();
                clearTableRelatedParameter(true);
                _cacheMetaDataItem = (EcogridMetaDataCacheItem) DataCacheManager.getCacheItem(
                        new MetadataComplete(), "MetaData " + emlFileFinalPath, endpoint,
                        EcogridMetaDataCacheItem.class.getName());
                if (_cacheMetaDataItem.isEmpty()) {
                    _cacheMetaDataItem.setEndPoint(endpoint);
                    _cacheMetaDataItem.setRecordId(emlFileFinalPath);
                    _cacheMetaDataItem.start();
                } else {
                    log.debug("in not empty============");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        } else if (attribute == sqlDef) {
            String sqlDefStr = ((Settable) attribute).getDefaultExpression();
            sqlDefStr = ((Settable) attribute).getExpression();
            if (sqlDefStr.length() > 0) {
                _hasSQLCommand = true;
                _queryDef = DBQueryDefParserEmitter.parseQueryDef(_schemaDef, sqlDefStr);
                _columns = _queryDef.getSelects();
                generteLabelListAndTypteListFromColumns();
                reconfigurePorts("Sql attribute changed");
            } else if (_dataOutputFormat instanceof Eml200DataOutputFormatField) {
                // if sql command is empty, we will think it will be select *
                // from selected table
                if (_selectedTableEntity != null) {
                    _columns = _selectedTableEntity.getFields();
                    generteLabelListAndTypteListFromColumns();
                    reconfigurePorts("Sql attribute changed");
                }
            }

        } else if (attribute == schemaDef && !_ignoreSchemaChange) {
            // NOTE: We may skip setting it here because _ignoreSchemaChange
            // may be true
            String schemaDefStr = ((Settable) schemaDef).getExpression();

            // MOML may have a blank definition
            if (schemaDefStr.length() > 0) {
                _schemaDefinitionIsSet = true; // remember that we have been
                // set by the MOML

                log.debug("schemaDef >>" + schemaDefStr + "<<");

                _schemaDef = DBSchemaParserEmitter.parseSchemaDef(schemaDefStr);
            }

        } else if (attribute.getName().equals("checkVersion")) {
            log.debug("=========================change checkVersion");
            // use the endpoint to determine where to search for the most recent
            // version
            this.checkForMostRecentRecordId(true);
        } else if ((attribute.getName().equals(ResultRecord.RECORDID)
                || attribute.getName().equals(ResultRecord.ENDPOINT)) && this.hasConnectionValues()
                && !(attribute.getContainer().getContainer() instanceof ResultTreeRoot)) {
            log.debug("=========================change recordid or endpoints");
            if (getRecordId() != null && getEndpoint() != null) {
                _icon.setBusy();
                // start over!
                clearTableRelatedParameter(false);
                //_entityList = new Vector<Entity>();
                _cacheMetaDataItem = (EcogridMetaDataCacheItem) DataCacheManager.getCacheItem(
                        new MetadataComplete(), "MetaData " + getRecordId(), getEndpoint(),
                        EcogridMetaDataCacheItem.class.getName());
                if (_cacheMetaDataItem.isEmpty()) {
                    _cacheMetaDataItem.setEndPoint(getEndpoint());
                    _cacheMetaDataItem.setRecordId(getRecordId());
                    _cacheMetaDataItem.start();
                }
            }

        } else if (attribute == dataOutputFormat) {
            String format = ((Settable) attribute).getExpression();
            log.debug("=========================change dataOutputFormat " + format);
            String strDataOutputFormat = dataOutputFormat.stringValue();
            _dataOutputFormat = Eml200DataOutputFormatFactory.newInstance(strDataOutputFormat, this);
            reconfigurePorts("Output type changed");

        } else if (attribute == selectedEntity) {
            // reset selected entity
            String selectedEntityName = ((Settable) attribute).getExpression();
            log.debug("=========================selected entity " + selectedEntityName);
            setSelectedEntityValue(true);
        }
    }

    /**
     * This method allows default documentation to be added to the actor
     * specified in the parameter. The KeplerDocumentation is retrieved from the
     * 'EML 2 Dataset' that exists in the actor library. The default
     * documentation is only loaded once since it will likely remain quite
     * static. If the given actor instance already contains the
     * KeplerDocumentation attribute, the existing attribute is preserved
     * (nothing is changed).
     * 
     * @param emlActor
     *            the instance to which documentation will be added
     */
    public static void generateDocumentationForInstance(Eml200DataSource emlActor) {
        try {
            // only look up the documentation only once
            if (defaultDocumentation == null) {
                Iterator cacheIter = CacheManager.getInstance().getCacheObjectIterator();
                while (cacheIter.hasNext()) {
                    Object co = cacheIter.next();
                    // is this an actor cache object?
                    if (co instanceof ActorCacheObject) {
                        ActorCacheObject aco = (ActorCacheObject) co;
                        // for this class?
                        if (aco.getClassName().equals(emlActor.getClassName())) {
                            // get the metadata
                            ActorMetadata am = aco.getMetadata();
                            // get the default documentation from the metadata
                            defaultDocumentation = am.getDocumentationAttribute();
                            log.debug("looked up default KeplerDocumentation contained in " + am.getName());
                            break;
                        }
                    }
                }
            }

            // add the documentation for this actor if it is not there already
            if (emlActor.getAttribute("KeplerDocumentation") == null) {
                // make an instance of the documentation attribute for the input
                // actor
                KeplerDocumentationAttribute keplerDocumentation = new KeplerDocumentationAttribute(emlActor,
                        "KeplerDocumentation");

                // copy the default and set it for this one
                keplerDocumentation.createInstanceFromExisting(defaultDocumentation);
                keplerDocumentation.setContainer(emlActor);
                log.debug("set the KeplerDocumentation for actor instance: " + emlActor.getName());

            }
        } catch (Exception e) {
            log.error("error encountered whilst generating default documentation for actor instance: "
                    + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * First checks if checkVersion parameter is selected. If it is selected the
     * Ecogrid is checked for a more recent version of the recordid. If there is
     * a newer version available, user is asked if they would like to use the
     * newer version in the workflow. If they really do want to use the newer
     * version, the most recent recorid is set in the actor attributes.
     * 
     */
    private void checkForMostRecentRecordId(boolean prompt) {

        // check if we even want to do this for the actor
        boolean boolCheckVersion = false;
        try {
            boolCheckVersion = ((BooleanToken) this.checkVersion.getToken()).booleanValue();
            log.debug("checkVersion flag=" + boolCheckVersion);
        } catch (IllegalActionException e1) {
            log.error("could not parse checkVersion parameter");
            e1.printStackTrace();
        }
        if (!boolCheckVersion) {
            checkVersionPromptCount = 0; // ask again if they turn it on later
            return;
        }

        // check for newer version of the eml
        String mostRecentRecordId = getMostRecentRecordId();

        // peek
        log.debug("Original recordId=" + getRecordId() + " - Most recent recordId=" + mostRecentRecordId);
        // different?
        if (mostRecentRecordId != null && !mostRecentRecordId.equalsIgnoreCase(getRecordId())) {

            // are we prompting?
            if (prompt) {
                if (checkVersionPromptCount < 1) {
                    boolean response = MessageHandler.yesNoQuestion("This workflow uses an old version of: "
                            + getName() + "\nCurrent workflow version: " + getRecordId()
                            + "\nMost recent repository version: " + mostRecentRecordId
                            + "\nWould you like to update the workflow to use the most recent version?"
                            + "\nNOTE:  Both the data and data structure can vary widely from version to version."
                            + "\nNewer version available - " + getName());

                    if (response == true) {
                        // reset the check counter for 'yes' answer so that we'll be
                        // able to ask again later
                        checkVersionPromptCount = 0;
                        setRecordId(mostRecentRecordId);
                    } else {
                        // I told you once, and i won't tell you again, I said NO!
                        checkVersionPromptCount++;
                    }
                }
            } else {
                setRecordId(mostRecentRecordId);
            }
        }
    }

    /**
     * This method will clear sql, schema and selected entity parameters in the
     * configure window.
     */
    private void clearTableRelatedParameter(boolean all) throws ptolemy.kernel.util.IllegalActionException {
        // clean up entity list and sql and schema attribute
        _entityList = new Vector();
        if (all) {
            selectedEntity.setExpression("");
        }
        selectedEntity.removeAllChoices();
        _queryDef = new DBQueryDef();
        sqlDef.setExpression("");
        _schema = new DSSchemaDef();
        schemaDef.setExpression("");
    }

    /**
     * Creates the schema definition from the incoming data columns
     */
    private void createSchemaFromData(Entity tableEntity) {
        try // XXX shouldn't catch this exception here
        {
            // _schemaDefinitionIsSet gets set when the schema has come
            // from the MOML
            // So if it is false here, then we set the attribute from the data.
            //
            // If after this the attr gets set from the MOML it will override
            // what we set here.
            // Entity tableEntity = dataItem.getEntity();
            DataCacheObject dataItem = tableEntity.getDataCacheObject();
            if (tableEntity != null) {
                _schema.addTable(tableEntity);
                _schemaDef = _schema;

                DBTableNameResolver nameResolver = new DBTableNameResolver();
                try {
                    tableEntity = nameResolver.resolveTableName(tableEntity);
                } catch (Exception e) {
                }
                boolean refresh = false;
                if (tableEntity.isSimpleDelimited()) {
                    // for simple dimilter, we can use text table
                    _tableGenerator = new DBTablesGenerator(tableEntity, dataItem.getBaseFileName(), refresh);
                } else {
                    // for complex format, we should use inpustream to create
                    // table
                    InputStream input = dataItem.getDataInputStream();
                    _tableGenerator = new DBTablesGenerator(tableEntity, input, refresh);
                }

                _tableGenerator.run(); // don't do thread

                String schemaDefXML = DBSchemaParserEmitter.emitXML(_schemaDef);
                _ignoreSchemaChange = true;
                ((Settable) schemaDef).setExpression(schemaDefXML);
                _ignoreSchemaChange = false;
            }
        } catch (IllegalActionException e) {
            log.debug("In createSchemaFromData: " + e);
        }

    }

    public void preview() {

        String displayText = "PREVIEW NOT IMPLEMENTED FOR THIS ACTOR";
        JFrame frame = new JFrame(this.getName() + " Preview");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        JPanel panel = new JPanel(new BorderLayout());
        JScrollPane scrollPane = null;
        JTable jtable = null;

        try {

            // set everything up (datawise)
            this.initialize();

            // check the entity - different displays for different formats
            // Compressed file
            if (this._selectedTableEntity.getHasGZipDataFile() || this._selectedTableEntity.getHasTarDataFile()
                    || this._selectedTableEntity.getHasZipDataFile()) {
                displayText = "Selected entity is a compressed file.  \n"
                        + "Preview not implemented for output format: " + this.dataOutputFormat.getExpression();
                if (this._dataOutputFormat instanceof Eml200DataOutputFormatUnzippedFileName) {
                    Eml200DataOutputFormatUnzippedFileName temp = (Eml200DataOutputFormatUnzippedFileName) this._dataOutputFormat;
                    displayText = "Files: \n";
                    for (int i = 0; i < temp.getTargetFilePathInZip().length; i++) {
                        displayText += temp.getTargetFilePathInZip()[i] + "\n";
                    }
                }

            }
            // SPATIALRASTERENTITY or SPATIALVECTORENTITY are "image entities"
            // as far as the parser is concerned
            else if (this._selectedTableEntity.getIsImageEntity()) {
                // use the content of the cache file
                displayText = new String(this.getSelectedCachedDataItem().getData());
            }
            // TABLEENTITY
            else {
                // holds the rows for the table on disk with some in memory
                String vectorTempDir = DotKeplerManager.getInstance().getCacheDirString();
                // + "vector"
                // + File.separator;
                PersistentVector rowData = new PersistentVector(vectorTempDir);

                // go through the rows and add them to the persistent vector
                // model
                Vector row = this.gotRowVectorFromSource();
                while (!row.isEmpty()) {
                    rowData.addElement(row);
                    row = this.gotRowVectorFromSource();
                }
                // the column headers for the table
                Vector columns = this.getColumns();

                /*
                 * with java 6, there is a more built-in sorting mechanism that
                 * does not require the custom table sorter class
                 */
                TableModel tableModel = new PersistentTableModel(rowData, columns);
                TableSorter tableSorter = new TableSorter(tableModel);
                jtable = new JTable(tableSorter) {
                    // make this table read-only by overriding the default
                    // implementation
                    public boolean isCellEditable(int row, int col) {
                        return false;
                    }
                };
                // sets up the listeners for sorting and such
                tableSorter.setTableHeader(jtable.getTableHeader());
                // set up the listener to trash persisted data when done
                frame.addWindowListener(new PersistentTableModelWindowListener((PersistentTableModel) tableModel));
            }
        } catch (Exception e) {
            displayText = "Problem encountered while generating preview: \n" + e.getMessage();
            log.error(displayText);
            e.printStackTrace();
        }

        // make sure there is a jtable, otherwise show just a text version of
        // the data
        if (jtable != null) {
            jtable.setVisible(true);
            // jtable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            scrollPane = new JScrollPane(jtable);
        } else {
            JTextArea textArea = new JTextArea();
            textArea.setColumns(80);
            textArea.setText(displayText);
            textArea.setVisible(true);
            scrollPane = new JScrollPane(textArea);
        }
        scrollPane.setVisible(true);
        panel.setOpaque(true);
        panel.add(scrollPane, BorderLayout.CENTER);
        frame.setContentPane(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    /**
     * Get a URL pointer to the documentation for this data source. The XML
     * source of the EML document is retrieved from the cache, and then passed
     * to an XSLT parser to be transformed into HTML format, which is saved in a
     * temporary file. The URL of the temporary file containing the HTML result
     * is returned.
     * 
     * @return URL the URL of the HTML file containing the documentation
     */
    public URL getDocumentation() {

        String namespace = getNamespace();
        if (namespace == null) {
            namespace = EML2MetadataSpecification.EML200NAMESPACE;
        }
        log.debug("The name space is " + namespace);
        URL htmlUrl = null;
        // Get the metadata XML document and transform it to html
        if (_cacheMetaDataItem.isReady()) {
            try {
                String htmlFileName = _cacheMetaDataItem.getBaseFileName() + HTMLEXTENSION;
                InputStream is = _cacheMetaDataItem.getDataInputStream();
                InputStreamReader source = new InputStreamReader(is);
                htmlUrl = StaticUtil.getMetadataHTMLurl(source, namespace, htmlFileName);

            } catch (Exception fnfe) {
                log.debug("Could not open temporary output file.");
            }
        }
        return htmlUrl;
    }

    /**
     * Creates the delimtedReader from the data item inputstream
     */
    private void createDelimitedReader() throws Exception {
        /*
         * String data = aDataStr.toString(); log.debug("-----------------\n" +
         * data + "\n-----------------\n", 2);
         */

        // log.debug("entityhash: " + entityhash.toString());
        if (_selectedTableEntity.isSimpleDelimited()) {
            boolean stripHeaderLine = true;
            boolean isLenientBool = ((BooleanToken) this.isLenient.getToken()).booleanValue();
            _simpleDelimitedReader = new DelimitedReader(_reader, _selectedTableEntity.getAttributes().length,
                    _selectedTableEntity.getDelimiter(), _selectedTableEntity.getNumHeaderLines(),
                    _selectedTableEntity.getRecordDelimiter(), _selectedTableEntity.getNumRecords(),
                    stripHeaderLine);
            _simpleDelimitedReader.setLenient(isLenientBool);
        } else {
            _complexFormatReader = new TextComplexFormatDataReader(_reader, _selectedTableEntity);
        }

        // _dataVectors = dr.getTokenizedData(true);
    }

    /**
     * Returns a Ptolemly type for a given Kepler DataSource Type
     * 
     * @param aType
     *            DataSource type
     * @return Ptolemy type
     */
    BaseType getBaseType(String aType) {
        BaseType type = (BaseType) _TypeHash.get(aType);
        if (type == null) {
            return BaseType.UNKNOWN;
        }
        return type;
    }

    /**
     * Inner class used for completion notification of EcoGridMetaDataCacheItem
     * objects.
     */
    private class MetadataComplete implements DataCacheListener {

        public void complete(DataCacheObject aItem) {
            log.debug("MetadataComplete: " + this);

            try {
                aItem.removeListener(this);

                if (!aItem.isReady()) {
                    log.error("Unable to download MetaData");
                    /*
                     * if (SHOWGUIERROR) { throw new
                     * IllegalActionException(this,
                     * "Unable to download MetaData");
                     * 
                     * }
                     */
                    MessageHandler.error("Unable to download MetaData");
                    _icon.setError();
                    return;
                }

                try {
                    parsePackage(new InputStreamReader(new FileInputStream(new File(aItem.getAbsoluteFileName()))));
                } catch (Exception e) {
                    log.error("Exception occurred during MetaDataCompletion", e);
                    /*
                     * if (SHOWGUIERROR) { throw new
                     * IllegalActionException(this,
                     * "Unable to parse the MetaData: " +e.getMessage() ,
                     * "alert", JOptionPane.ERROR_MESSAGE); }
                     */
                    MessageHandler.error("Unable to parse the MetaData: " + e.getMessage());
                    _icon.setError();
                }
                // if no sql command, we need set columns
                if (!_hasSQLCommand) {
                    log.debug("There is no sql command attribute and set up columns in compelete method");
                    _columns = _selectedTableEntity.getFields();
                }

                generteLabelListAndTypteListFromColumns();
                reconfigurePorts("Metadata Complete");

            } finally {
                _metadataCompleted.release();
            }
            // System.out.println("metadata complete !!!!!!!!!!!!!!!!1");
        }
    }

    // ------------------------------------------------------------------------
    // -- DataCacheListener
    // ------------------------------------------------------------------------

    public void complete(DataCacheObject aItem) {
        log.debug("complete: " + this);

        log.debug("Class of aItem " + aItem.getClass().getName());
        aItem.removeListener(this);
        if (aItem.isReady()) {
            log.debug("aItem is instanceof EcogridDataCacheItem");

            EcogridDataCacheItem item = (EcogridDataCacheItem) aItem;
            String entityIdentifier = item.getEntityIdentifier();
            int index = lookupEntityListName(entityIdentifier);
            Entity entity = null;
            if (index != INDEXFORNOTFOUND) {
                entity = (Entity) _entityList.elementAt(index);
                entity.setDataCacheObject(item);
            }

            if (entity != null && !entity.getIsImageEntity()) {

                createSchemaFromData(entity);

            }
        } else if (aItem.isError()) {
            log.debug("In failed download path");
            EcogridDataCacheItem item = (EcogridDataCacheItem) aItem;
            String entityIdentifier = item.getEntityIdentifier();
            int index = lookupEntityListName(entityIdentifier);
            Entity entity = null;
            if (index != INDEXFORNOTFOUND) {
                entity = (Entity) _entityList.elementAt(index);
            }
            // because fail to download, set null as data cache item
            entity.setDataCacheObject(null);
            _numberOfFailedDownloadEntities++;
            failedDownloadEntityName.add(entityIdentifier);
        }
        // Decrement the number of downloads completed.
        _entityCountDown.release();

        // Check for completion
        finished();
    }

    /**
     * Parses the package and fills in the names and types arrays.
     * 
     * @param eml
     * @throws IllegalActionException
     */
    private void parsePackage(Reader eml) throws IllegalActionException {
        Eml200Parser eml2parser;

        try { // parse the package for the names and types of the atts
            log.debug("creating parser");
            eml2parser = new Eml200Parser();
            log.debug("parsing...");
            eml2parser.parse(new InputSource(eml));
            if (getNamespace() == null) {
                log.debug("the namespace from parser is " + eml2parser.getNameSpace()
                        + " and set it to ResultRecord");
                setNamespace(eml2parser.getNameSpace());
            }
            // get if this package has image entity
            // _hasImageEntity = eml2parser.getHasImageEntity();
            // log.debug("This pakcage has image entity " + _hasImageEntity);
            log.debug("Done parsing");
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalActionException("Error parsing the eml package: " + e.getMessage());
        }

        getData(eml2parser); // fills in the _dataVectors data member
        setOptionsForSelectedEntityAttribute();

        // finished();
    }

    private void finished() {

        if (_entityCountDown.currentCount() > 0) {
            return;
        }

        try {
            // this method will set up columns info if not sql command
            setSelectedEntityValue(false);

        } catch (Exception e) {
            log.debug("The error in set up selected entity is ", e);

        }
        if (_numberOfFailedDownloadEntities == 0) {
            _icon.setReady();
        } else {
            log.error("Some downloads failed");
            StringBuffer entityNameList = new StringBuffer();
            for (int i = 0; i < failedDownloadEntityName.size(); i++) {
                if (entityNameList.length() > 0) {
                    entityNameList.append(", ");
                }
                String name = (String) failedDownloadEntityName.elementAt(i);

                entityNameList.append(name);
            }

            _icon.setError();

            //make an exception, but just use the message handler for it
            String msg = "Data entity/entities: " + entityNameList.toString()
                    + " failed to be downloaded, please check the data link in metadata";

            InternalErrorException exception = new InternalErrorException(this, null, "Download error");
            try {
                MessageHandler.warning(msg, exception);
            } catch (CancelException e) {
                //do nothing
            }

        }

    }

    /**
     * get the data based on the contents of the package
     * 
     * @param parser
     *            the parser that has already parsed the package.
     */
    private void getData(Eml200Parser parser) throws IllegalActionException {
        if (parser != null) {
            _numberOfEntities = parser.getEntityCount();
            _entityCountDown = new CountDown(_numberOfEntities);
            /*
             * if (parser.getEntityCount() > 1) { throw new
             * IllegalActionException(
             * "Currently this parser only deals with one entity. " +
             * "Please use a package with only one entity.");
             */
            if (_numberOfEntities == 0) {
                throw new IllegalActionException("There must be at least one entity in the EML package.");
            }
            Hashtable entityList = parser.getEntityHash();
            // initialize selected entity
            _selectedTableEntity = (Entity) entityList.get(entityList.keys().nextElement());
            // log.debug("entityhash: " + _entityList.toString());

            // start a thread to get data chachedItem
            Enumeration enu = entityList.elements();
            while (enu.hasMoreElements()) {

                Entity singleEntity = (Entity) enu.nextElement();
                log.debug("Adding Entity " + singleEntity);
                // System.out.println("Data URL = "+singleEntity.getURL());
                String dataurl = dataFilePath.getExpression();
                // check for absolute path - relative was not resolving correctly
                try {
                    URL url = dataFilePath.asURL();
                    if (url != null) {
                        dataurl = url.getPath();
                    }
                } catch (Exception e) {
                    // do nothing - just ignore it
                }
                // use the dataurl parameter if it has been entered
                // ie replace the url in the Entity object
                // use: to set local data file
                if (dataurl != null && dataurl.length() > 0) {
                    if (dataurl.startsWith("/")) {
                        dataurl = "file://" + dataurl;
                    } else if (dataurl.startsWith("file:/")) {
                        if ((!dataurl.startsWith("file://")) && (!dataurl.startsWith("file:///"))) {
                            dataurl = dataurl.replaceFirst("file:/", "file:///");
                        }
                    } else {
                        dataurl = "file:///" + dataurl;
                    }
                    singleEntity.setURL(dataurl);
                    // System.out.println("Data URL(1) = "+singleEntity.getURL());
                }
                _entityList.add(singleEntity);
                getDataItemFromCache(singleEntity);
            }

        }
    }

    /**
     * This method will start a thread to get the cached data item at tableEntity.getURL()
     * 
     * @param tableEntity
     */
    private void getDataItemFromCache(Entity tableEntity) {
        if (tableEntity == null) {
            log.debug("The table enity is null and couldn't get cached data item");
            return;
        }
        String fileURLStr = tableEntity.getURL();
        log.debug("Data URL is: " + fileURLStr);
        // we need to distinguish zip file and generate
        // String compression = tableEntity.getCompressionMethod();
        EcogridDataCacheItem cachedDataItem = null;
        String dataItemName = "Data " + fileURLStr + " from " + this.getEndpoint();
        if (tableEntity.getHasZipDataFile()) {
            log.debug("This is a zip data cacheItem");
            cachedDataItem = (EcogridZippedDataCacheItem) DataCacheManager.getCacheItem(this, dataItemName,
                    tableEntity.getName(), fileURLStr, EcogridZippedDataCacheItem.class.getName());
            // _isZipDataFile = true;
        } else if (tableEntity.getHasGZipDataFile()) {
            log.debug("This is a gzip data cacheItem");
            cachedDataItem = (EcogridGZippedDataCacheItem) DataCacheManager.getCacheItem(this, dataItemName,
                    tableEntity.getName(), fileURLStr, EcogridGZippedDataCacheItem.class.getName());
            if (tableEntity.getHasTarDataFile()) {
                log.debug("This is gizp and tar data cache item");
                cachedDataItem.setIsTarFile(true);
            }

        } else if (tableEntity.getHasTarDataFile()) {
            log.debug("This is a tar data cacheItem");
            cachedDataItem = (EcogridTarArchivedDataCacheItem) DataCacheManager.getCacheItem(this, dataItemName,
                    tableEntity.getName(), fileURLStr, EcogridTarArchivedDataCacheItem.class.getName());
        } else {
            log.debug("This is a uncompressed data cacheItem");
            cachedDataItem = (EcogridDataCacheItem) DataCacheManager.getCacheItem(this, dataItemName,
                    tableEntity.getName(), fileURLStr, EcogridDataCacheItem.class.getName());
        }
        if (cachedDataItem.isEmpty()) {
            String endPoint = null;
            try {
                // System.out.println("before get endpoint .........");
                endPoint = this.getEndpoint();
                // System.out.println("after get endpoint ............");
            } catch (Exception e) {
                log.debug("the exeption for get endpoint is " + e.getMessage());
            }

            if (endPoint == null) {
                cachedDataItem.setEndPoint(ENDPOINT);
            } else {
                cachedDataItem.setEndPoint(endPoint);
            }
            // add entity identifier order to track the cachedata item associated
            // with which entity(In complete method)
            cachedDataItem.setEntityIdentifier(tableEntity.getName());
            cachedDataItem.start();
        }

    }

    /**
     * This method will set options for "Selected Entity" after getting all data
     * entities. The options will be the list of entity names.
     */
    private void setOptionsForSelectedEntityAttribute() {
        if (_entityList != null) {
            int length = _entityList.size();
            for (int i = 0; i < length; i++) {
                Entity entity = (Entity) _entityList.elementAt(i);
                String entityName = entity.getName();
                if (entityName != null && !entityName.trim().equals("")) {
                    selectedEntity.addChoice(entityName);
                }

            }

        }
    }

    /**
     * Method to set up selected entity. If Attribute "selectedEntity" already
     * has value, look up the enitytList and found the selected entity. If
     * attribute "selectedEntity doesn't have any value, we choose the index 0
     * as selected entity.
     */
    private void setSelectedEntityValue(boolean fromAttributeChange) throws IllegalActionException {

        String selectedEntityName = selectedEntity.stringValue();
        log.debug("The selected entity name is " + selectedEntityName);
        if (!_entityList.isEmpty()) {

            if (selectedEntityName != null && !selectedEntityName.trim().equals("")) {
                // already has a selected entity in momol
                log.debug("There is a selected entity in actor");
                int selectedIndex = lookupEntityListName(selectedEntityName);
                // System.out.println("index of selected entity is "+selectedIndex);
                if (selectedIndex == INDEXFORNOTFOUND) {
                    throw new IllegalActionException("The selected Entity in momol cound't be found");
                } else {
                    _selectedTableEntity = (Entity) _entityList.elementAt(selectedIndex);
                    if (!_hasSQLCommand) {
                        _columns = _selectedTableEntity.getFields();
                    }
                    _selectedCachedDataItem = (EcogridDataCacheItem) _selectedTableEntity.getDataCacheObject();
                    generteLabelListAndTypteListFromColumns();
                    reconfigurePorts("Selected Entity changed");
                }
            } else {
                // no selected enity in moml and we need selected one
                log.debug("There is NOT a selected entity in actor");
                _selectedTableEntity = (Entity) _entityList.elementAt(DEFAULTINDEX);
                _selectedCachedDataItem = (EcogridDataCacheItem) _selectedTableEntity.getDataCacheObject();
                reconfigurePorts("Selected Entity changed");
                String entityName = _selectedTableEntity.getName();
                log.debug("set the default entity name " + entityName + "because the there is no selected entity");
                selectedEntity.setExpression(entityName);
                if (!fromAttributeChange) {
                    log.debug("send a moml request for adding selected Entity parameter");
                    StringBuffer buffer = new StringBuffer();
                    buffer.append("<property name=\"");
                    buffer.append("selectedEntity");
                    buffer.append("\" class=\"");
                    buffer.append(selectedEntity.getClassName());
                    buffer.append("\" value=\"");
                    buffer.append(entityName);
                    buffer.append("\"/>");
                    String moml = buffer.toString();
                    log.debug("The moml string is " + moml);
                    NamedObj container = (NamedObj) this.getContainer();
                    NamedObj composite = (NamedObj) container.getContainer();
                    MoMLChangeRequest request = new MoMLChangeRequest(this, this, moml.toString());
                    request.setUndoable(true);
                    this.requestChange(request);

                }
            }
        }
    }

    /**
     * Method to find the entity index in EntityList which has the same name as
     * the given string. If no entity index is found, -1 will be returned.
     */
    private int lookupEntityListName(String givenString) {
        log.debug("Looking for entity named " + givenString);
        int index = INDEXFORNOTFOUND;
        if (givenString != null && !givenString.trim().equals("")) {
            int size = _entityList.size();
            for (int i = 0; i < size; i++) {
                Entity entity = (Entity) _entityList.elementAt(i);
                String entityName = entity.getName();
                if (entityName != null && !entityName.trim().equals("") && entityName.equals(givenString)) {
                    index = i;

                }
            }
        }
        log.debug("The selected index is " + index);
        return index;
    }

    /**
     * This method will generate selected column list.
     */
    private void generteLabelListAndTypteListFromColumns() {

        if (_columns != null) {
            int size = _columns.size();
            _selectedColumnLabelList = new String[size];
            _selectedColumnTypeList = new Type[size];
            for (int i = 0; i < size; i++) {
                DSTableFieldIFace column = (DSTableFieldIFace) _columns.elementAt(i);
                _selectedColumnLabelList[i] = column.getName();
                String type = column.getDataType();
                _selectedColumnTypeList[i] = getBaseType(type);

            }
        }

    }

    /**
     * Method to transform a string array to token array based on given type.
     */
    static Token[] transformStringVectorToTokenArray(Vector stringVector, Type type, Vector missingValuecode)
            throws IllegalActionException {
        if (stringVector == null) {
            return null;
        }
        int size = stringVector.size();
        Token[] columnToken = new Token[size];
        for (int j = 0; j < size; j++) {
            String eleStr = (String) stringVector.elementAt(j);
            log.debug("The column value " + eleStr);
            Token val = transformStringToToken(eleStr, type, missingValuecode, null);
            columnToken[j] = val;
        }

        return columnToken;
    }

    /**
     * Method to transform a string to token based on given type and
     * missingValue.
     */
    static Token transformStringToToken(String eleStr, Type type, Vector missingValue, String columnName)
            throws IllegalActionException {
        Token val = null;
        if (missingValue != null && !missingValue.isEmpty()) {
            if (missingValue.contains(eleStr)) {
                eleStr = null;
            }
        }
        String elementError = "Element \"";
        String errorMessage1 = "\" couldn't be in the ";
        String errorMessage2 = " column " + columnName + ". It probably is a missing value code, however metadata "
                + "doesn't describe it. Please make a double check.";
        // find the data type for each att
        if (type == BaseType.INT) {

            if (eleStr != null && !eleStr.equals("")) {
                try {
                    val = new IntToken(new Integer(eleStr).intValue());
                } catch (NumberFormatException e) {
                    throw (new IllegalActionException(
                            elementError + eleStr + errorMessage1 + "integer" + errorMessage2));
                }
            } else {
                // eleStr = null;
                val = IntToken.NIL;
                // val.nil();
            }

        } else if (type == BaseType.DOUBLE) {
            if (eleStr != null && !eleStr.equals("")) {
                try {
                    val = new DoubleToken(new Double(eleStr).doubleValue());
                } catch (NumberFormatException e) {
                    throw (new IllegalActionException(
                            elementError + eleStr + errorMessage1 + "numerical" + errorMessage2));
                }
            } else {
                // eleStr = null;
                val = DoubleToken.NIL;
            }

        } else if (type == BaseType.LONG) {
            if (eleStr != null && !eleStr.equals("")) {
                try {
                    val = new LongToken(new Long(eleStr).longValue());
                } catch (NumberFormatException e) {
                    throw (new IllegalActionException(
                            elementError + eleStr + errorMessage1 + "numerical" + errorMessage2));
                }
            } else {
                // eleStr = null;
                val = LongToken.NIL;
                // val.nil();
            }

        } else if (type == BaseType.STRING) {
            if (eleStr != null) {
                val = new StringToken(eleStr);
            } else {
                // eleStr = "nil";
                val = StringToken.NIL;
                // val.nil();
            }

        } else {
            val = new IntToken(0);
        }
        return val;
    }

    /**
     * Callback method that indicates that the workflow is being stopped. This
     * method is executed from the Director when the user presses the "stop"
     * button. All this does is release the execution thread if it happens to be
     * blocked in the initialize method waiting for _metaDataCompleted or
     * _entityCountDown to complete.
     */
    public void stop() {
        log.debug("Stopping");

        synchronized (_metadataCompleted) {
            _metadataCompleted.notifyAll();
        }
        synchronized (_entityCountDown) {
            _entityCountDown.notifyAll();
        }
        // TODO Auto-generated method stub
        super.stop();
    }

    /**
     * This method will determine if the resultset is complete.
     * 
     * @return true if the result has been completely retrieved
     */
    public boolean isEndOfResultset() throws SQLException {
        if (_resultSet != null && _resultSet.getResultSet() != null && _resultSet.getResultSet().isAfterLast()) {
            return true;
        } else {
            return false;
        }
    }

    /*
     * This method will remove RecordDetails attribute from this entity. Those
     * attribute is useful for ResultRecord, but is useless for this class and
     * we wouldn't let it show up.
     */
    private void removeResultRecordDetailsAtrribute() throws IllegalActionException, NameDuplicationException {
        // System.out.println("at begining");
        List list = this.attributeList();
        if (list != null) {
            // System.out.println("attribute list is not null");
            for (int j = 0; j < list.size(); j++) {
                // System.out.println("the attribute list's size is "+recordDetailList.size());
                ptolemy.kernel.util.Attribute att = (ptolemy.kernel.util.Attribute) list.get(j);
                String attName = att.getName();
                // System.out.println("------- the attribute "+att.getName());
                Vector recordDetailList = this.getRecordDetailList();

                if (recordDetailList != null && attName != null && recordDetailList.contains(attName)) {
                    // System.out.println("------- remove the attribute "+att.getName());
                    att.setContainer(null);

                }
            }
        }

    }

    /**
     * First checks if checkVersion parameter is selected. If it is selected the
     * Ecogrid is checked for a more recent version of the recordid. If there is
     * a newer version available, user is asked if they would like to use the
     * newer version in the workflow. If they really do want to use the newer
     * version, the most recent recorid is set in the actor attributes.
     * 
     */
    private String getMostRecentRecordId() {

        // look up the identifcation service based on the query/get service
        EcoGridService queryService = EcoGridServicesController.getInstance().getService(getEndpoint());
        EcoGridService lsidService = EcoGridServicesController.getInstance()
                .getService(queryService.getServiceGroup(), EcoGridServicesController.IDENTIFIERSERVICETYPE);
        log.debug("identifier service endpoint: " + lsidService.getEndPoint());

        // check for newer version of the eml
        String mostRecentRecordId = null;
        try {

            // translate the recordId to an lsid
            KeplerLSID recordLsid = new KeplerLSID(getRecordId(), "kepler-project.org");
            log.debug("translated recordLsid=" + recordLsid);

            // look up the next revision
            IdentifierServiceClient lsidClient = new IdentifierServiceClient(lsidService.getEndPoint());
            KeplerLSID temp = new KeplerLSID(lsidClient.getNextRevision(recordLsid.toString()));
            log.debug("next recordLsid=" + temp);

            // subtract from the next revision to get the current latest
            mostRecentRecordId = temp.getNamespace() + "." + temp.getObject() + "."
                    + (temp.getRevision().longValue() - 1);
            log.debug("mostRecentRecordId=" + mostRecentRecordId);

        } catch (Exception e) {
            log.error("Problem looking up most recent record for id: " + getRecordId() + "\nError is: "
                    + e.getMessage());
            e.printStackTrace();
        }

        return mostRecentRecordId;
    }

    /**
    * Overwrite the method in Parent class -- ResultRecord
    */
    public Reader getFullRecord() {
        //System.out.println("in eml actor ==================");
        Reader recordReader = null;
        if (this.getEndpoint() != null) {
            //System.out.println("end point is not null");
            return super.getFullRecord();
        } else {
            //System.out.println("end point is === null");
            //System.out.println("final path is "+emlFileFinalPath);
            String endpoint = null;
            EcogridMetaDataCacheItem item = (EcogridMetaDataCacheItem) DataCacheManager.getCacheItem(
                    new MetadataComplete(), "MetaData " + emlFileFinalPath, endpoint,
                    EcogridMetaDataCacheItem.class.getName());
            if (item.isEmpty()) {
                //System.out.println(" in item is empty branch");
                item.setEndPoint(endpoint);
                item.setRecordId(emlFileFinalPath);
                item.start();
                while (!item.isError() && !item.isReady()) {
                    // do nothing, just waiting
                    //System.out.println("Waiting!!!!!!!!!!");
                }
            }
            // make sure the item is finished    
            // when it is ready
            InputStream stream = item.getDataInputStream();
            if (stream != null) {
                //System.out.println(" stream is not null");
                recordReader = new InputStreamReader(stream);
            }
            //System.out.println("the return reader is "+recordReader);
            return recordReader;
        }

    }
}