Projects >> IDV >>a676a4d7ca44b814558be367e71a84063247b606

Chunk
Conflicting content
<<<<<<< HEAD
/*
 * Copyright 1997-2012 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.data.imagery;


import edu.wisc.ssec.mcidas.AreaDirectory;

import edu.wisc.ssec.mcidas.AreaDirectoryList;
import ucar.unidata.data.CompositeDataChoice;
import edu.wisc.ssec.mcidas.AreaFileException;
import ucar.unidata.data.DataCategory;
import ucar.unidata.data.DataChoice;
import ucar.unidata.data.DataSelection;
import ucar.unidata.data.DataSourceDescriptor;
import ucar.unidata.data.DataSourceImpl;
import ucar.unidata.data.DirectDataChoice;
import ucar.unidata.util.CacheManager;
import ucar.unidata.util.IOUtil;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.PollingInfo;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.TwoFacedObject;

import ucar.visad.UtcDate;
import ucar.visad.data.AreaImageFlatField;

import visad.CommonUnit;
import visad.Data;
import visad.DateTime;
import visad.FunctionType;
import visad.MathType;
import visad.RealType;
import visad.Set;
import visad.VisADException;

import visad.data.DataRange;
import visad.data.mcidas.AreaAdapter;

import visad.meteorology.ImageSequence;
import visad.meteorology.ImageSequenceImpl;
import visad.meteorology.ImageSequenceManager;
import visad.meteorology.SingleBandedImage;

import visad.util.ThreadManager;


import java.io.File;

import java.rmi.RemoteException;

import java.text.SimpleDateFormat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.TreeMap;


/**
 * Abstract DataSource class for images files.
 *
 * @author IDV development team
 */
public abstract class ImageDataSource extends DataSourceImpl {

    /** Type of image, radar or satellite. Set by the chooser to disambiguate between types */
    public static final String PROP_IMAGETYPE = "prop.imagetype";

    /** radar type */
    public static final String TYPE_RADAR = "radar";

    /** satellite type */
    public static final String TYPE_SATELLITE = "satellite";

    /** satellite type */
    public static final String PROP_BANDINFO = "bandinfo";

    /** list of twod categories */
    private List twoDCategories;

    /** list of 2D time series categories */
    private List twoDTimeSeriesCategories;

    /** list of twod categories */
    private List bandCategories;

    /** list of 2D time series categories */
    private List bandTimeSeriesCategories;

    /** list of images */
    protected List imageList;

    /** list of image times */
    protected List imageTimes = new ArrayList();

    /** My composite */
    private CompositeDataChoice myCompositeDataChoice;

    /** children choices */
    private List myDataChoices = new ArrayList();


    /** current directories */
    private AreaDirectory[][] currentDirs;

    /** timeMap */
    private Hashtable timeMap = new Hashtable();



    /**
     *  The parameterless constructor for unpersisting.
     */
    public ImageDataSource() {}


    /**
     * Create a new ImageDataSource with a list of (String) images. These
     * can either be AREA files or ADDE URLs.
     *
     * @param descriptor       The descriptor for this data source.
     * @param images           Array of  file anmes or urls.
     * @param properties       The properties for this data source.
     */
    public ImageDataSource(DataSourceDescriptor descriptor, String[] images,
                           Hashtable properties) {
        super(descriptor, "Image data set", "Image data source", properties);
        if ( !initDataFromPollingInfo()) {
            setImageList(makeImageDescriptors(images));
        }
        setDescription(getImageDataSourceName());
    }



    /**
     * Create a new ImageDataSource with a list of (String) images. These
     * can either be AREA files or ADDE URLs.
     *
     * @param descriptor       The descriptor for this data source.
     * @param images           Array of  file anmes or urls.
     * @param properties       The properties for this data source.
     */
    public ImageDataSource(DataSourceDescriptor descriptor, List images,
                           Hashtable properties) {
        this(descriptor, StringUtil.listToStringArray(images), properties);
    }




    /**
     * Create a new ImageDataSource with the given {@link ImageDataset}.
     * The dataset may hold eight AREA file filepaths or ADDE URLs.
     *
     * @param descriptor    The descriptor for this data source.
     * @param ids           The dataset.
     * @param properties    The properties for this data source.
     */
    public ImageDataSource(DataSourceDescriptor descriptor, ImageDataset ids,
                           Hashtable properties) {
        super(descriptor, ids.getDatasetName(), "Image data source",
              properties);
        setImageList(new ArrayList(ids.getImageDescriptors()));
        setDescription(getImageDataSourceName());
    }


    /**
     * Reload data
     *
     * @param object  the object to reload
     * @param properties  the properties
     */
    public void reloadData(Object object, Hashtable properties) {
        if (object instanceof ImageDataset) {
            ImageDataset ids = (ImageDataset) object;
            setImageList(new ArrayList(ids.getImageDescriptors()));
        } else if (object instanceof List) {
            String[] images = StringUtil.listToStringArray((List) object);
            setImageList(makeImageDescriptors(images));
        } else {
            try {
                String[] images = (String[]) object;
                setImageList(makeImageDescriptors(images));
            } catch (Exception exc) {
                return;
            }
        }
        setDescription(getImageDataSourceName());
        reloadProperties(properties);
        reloadData();
    }


    /**
     * Handle when this data source gets new files to use at runtime (e.g., from isl)
     *
     * @param files List of files
     *
     */
    public void setNewFiles(List files) {
        setImageList(
            makeImageDescriptors(StringUtil.listToStringArray(files)));
    }




    /**
     * Update the state
     *
     * @param newObject  the new object
     * @param newProperties  the new properties
     */
    public void updateState(Object newObject, Hashtable newProperties) {
        super.updateState(newObject, newProperties);
        if (newObject instanceof ImageDataset) {
            ImageDataset ids = (ImageDataset) newObject;
            setImageList(new ArrayList(ids.getImageDescriptors()));
            setDescription(getImageDataSourceName());
        } else if (newObject instanceof List) {
            setTmpPaths((List) newObject);
        } else if (newObject instanceof String) {
            setTmpPaths(Misc.newList(newObject));
        }
    }


    /**
     * Get the paths for saving data files
     *
     * @return data paths
     */
    public List getDataPaths() {
        List             paths = new ArrayList();
        SimpleDateFormat sdf   = new SimpleDateFormat("_"
                                   + DATAPATH_DATE_FORMAT);
        try {
            for (int i = 0; i < imageList.size(); i++) {
                AddeImageDescriptor aid  = getDescriptor(imageList.get(i));
                String              path = aid.getSource();
                DateTime            dttm = (DateTime) timeMap.get(path);
                /*                if(dttm!=null) {
                    String dateString = sdf.format(ucar.visad.Util.makeDate(dttm));
                    if(path.indexOf(".area")>=0 && path.indexOf(dateString)==-1) {
                        path = path.replace(".area", dateString+".area");
                    }
                    System.err.println("path:" + path);
                    paths.add(new Object[]{path,path+dateString});
                    } else {*/
                paths.add(path);
                //                }
            }
        } catch (Exception exc) {
            throw new ucar.unidata.util.WrapperException(exc);
        }
        return paths;
    }


    /**
     * Override the init method for when this data source is unpersisted.
     * We simply check the imageList to see if this object came from a
     * legacy bundle.
     */
    public void initAfterUnpersistence() {
        super.initAfterUnpersistence();
        List tmp = getTmpPaths();
        if (tmp != null) {
            imageList = new ArrayList();
            for (int i = 0; i < tmp.size(); i++) {
                imageList.add(new AddeImageDescriptor(tmp.get(i).toString()));
            }
        }
        if ((imageList != null) && (imageList.size() > 0)
                && (imageList.get(0) instanceof String)) {
            List tmpList = imageList;
            imageList = new ArrayList();
            for (int i = 0; i < tmpList.size(); i++) {
                imageList.add(
                    new AddeImageDescriptor(tmpList.get(i).toString()));
            }
        }
        initDataFromPollingInfo();
    }



    /**
     * Can this data source cache its
     *
     * @return can cache data to disk
     */
    public boolean canCacheDataToDisk() {
        return true;
    }




    /**
     * Is this data source capable of saving its data to local disk
     *
     * @return Can save to local disk
     */
    public boolean canSaveDataToLocalDisk() {
        if (isFileBased()) {
            return false;
        }
        List bandInfos =
            (List) getProperty(PROP_BANDINFO, (Object) null);
     */
    public List getCompositeDataChoices(
        if ((bandInfos == null) || (bandInfos.size() == 0)) {
            return true;
        }
        if (bandInfos.size() > 1) {
            return false;
        }
        List l = bandInfos.get(0).getCalibrationUnits();
        if (l.size() > 1) {
            return false;
        }
        return true;
    }

    /**
     * Save files to local disk
     *
     * @param prefix destination dir and file prefix
     * @param loadId For JobManager
     * @param changeLinks Change internal file references
     *
     * @return Files copied
     *
     * @throws Exception On badness
     */
    protected List saveDataToLocalDisk(String prefix, Object loadId,
                                       boolean changeLinks)
            throws Exception {
        List             urls     = new ArrayList();
        List             suffixes = new ArrayList();
        SimpleDateFormat sdf      = new SimpleDateFormat("_"
                                   + DATAPATH_DATE_FORMAT);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        for (int i = 0; i < imageList.size(); i++) {
            AddeImageDescriptor aid  = getDescriptor(imageList.get(i));
            String              url  = aid.getSource();
            DateTime            dttm = (DateTime) timeMap.get(url);
            if (dttm != null) {
                suffixes.add(sdf.format(ucar.visad.Util.makeDate(dttm))
                             + ".area");
            } else {
                suffixes.add(i + ".area");
            }
            urls.add(url);
        }
        List newFiles = IOUtil.writeTo(urls, prefix, suffixes, loadId);
        // System.err.println ("files:" + newFiles);
        if (newFiles == null) {
            return null;
        }
        if (changeLinks) {
            imageList = newFiles;
        }
        return newFiles;
    }






    /**
     * Method for intializing the data.
     *
     *
     * @return result
     */
    protected boolean initDataFromPollingInfo() {
        PollingInfo pollingInfo = getPollingInfo();
        if ( !pollingInfo.getForFiles()
                || !pollingInfo.doILookForNewFiles()) {
            return false;
        }
        imageList = new ArrayList();
        List files = pollingInfo.getFiles();
        for (int i = 0; i < files.size(); i++) {
            imageList.add(new AddeImageDescriptor(files.get(i).toString()));
        }
        return true;
    }


    /**
     * The user changed the properties. Update me.
     */
    protected void propertiesChanged() {
        PollingInfo pollingInfo = getPollingInfo();
        if (pollingInfo.doILookForNewFiles()) {
            List newSources = pollingInfo.getFiles();
            if (newSources.size() != imageList.size()) {
                initDataFromPollingInfo();
                dataChoices = null;
                notifyDataChange();
            }
        }
        super.propertiesChanged();
    }



    /**
     * Make an ImageDataset from an array of ADDE URLs or AREA file names
     *
     * @param addeURLs  array of ADDE URLs
     *
     * @return ImageDataset
     */
     *
    public static ImageDataset makeImageDataset(String[] addeURLs) {
        AddeImageDescriptor[] aids = new AddeImageDescriptor[addeURLs.length];
        for (int i = 0; i < addeURLs.length; i++) {
            aids[i] = new AddeImageDescriptor(addeURLs[i]);
        }
        return new ImageDataset("Image data set", Arrays.asList(aids));
    }


    /**
     * Make a list of image descriptors
     *
     * @param addeURLs  array of ADDE URLs
     *
     * @return ImageDataset
     */
    public static List makeImageDescriptors(String[] addeURLs) {
        List descriptors = new ArrayList();
        for (int i = 0; i < addeURLs.length; i++) {
            descriptors.add(new AddeImageDescriptor(addeURLs[i]));
        }
        return descriptors;
    }



    /**
     * Get the location where we poll.
     *
     * @return Directory to poll on.
     */
    protected List getLocationsForPolling() {
        if ( !isFileBased()) {
            return null;
        }
        List files = new ArrayList();
        for (int i = 0; i < imageList.size(); i++) {
            AddeImageDescriptor aid = getDescriptor(imageList.get(i));
            files.add(aid.getSource());
        }
        return files;
    }


    /**
     * Are we getting images from a file or from adde
     *
     * @return is the data from files
     */
    protected boolean isFileBased() {
        if ((imageList == null) || (imageList.size() == 0)) {
            return false;
        }
        AddeImageDescriptor aid = getDescriptor(imageList.get(0));
        return aid.isFromFile();
    }


    /**
     * A utility method that helps us deal with legacy bundles that used to
     * have String file names as the id of a data choice.
     *
     * @param object     May be an AddeImageDescriptor (for new bundles) or a
     *                   String that is converted to an image descriptor.
     * @return The image descriptor.
     */
    public AddeImageDescriptor getDescriptor(Object object) {
        if (object == null) {
            return null;
        }
        if (object instanceof DataChoice) {
            object = ((DataChoice) object).getId();
        }
        if (object instanceof ImageDataInfo) {
            int index = ((ImageDataInfo) object).getIndex();
            if (index < myDataChoices.size()) {
                DataChoice dc        = (DataChoice) myDataChoices.get(index);
                Object     tmpObject = dc.getId();
                if (tmpObject instanceof ImageDataInfo) {
                    return ((ImageDataInfo) tmpObject).getAid();
                }
            }
            return null;
            //            return ((ImageDataInfo) object).getAid();
        }

        if (object instanceof AddeImageDescriptor) {
            return (AddeImageDescriptor) object;
        }
        return new AddeImageDescriptor(object.toString());
    }





    /**
     * This is used when we are unbundled and we may have different times than when we were saved.
     * Use the current set of data choices.
     *
     * @param compositeDataChoice The composite
     * @param dataChoices Its choices
     *
     * @return The  current choices
     * single time step data with band information.
            CompositeDataChoice compositeDataChoice, List dataChoices) {
        //Force  creation of data choices
        getDataChoices();
        return !(hasBandInfo(compositeDataChoice))
               ? myDataChoices
               : dataChoices;
    }



    /**
     * A hook for the derived classes to return their specific name (eg,
     * ADDE data source, McIDAS data source.
     *
     * @return The name of this data source.
     */
    public abstract String getImageDataSourceName();

    /**
     * Return the list of {@link AddeImageDescriptor}s that define this
     * data source.
     *
     * @return The list of image descriptors.
     */
    public List getImageList() {
        return imageList;
    }

    /**
     * Set the list of {@link AddeImageDescriptor}s that define this data
     * source.
     *
     * @param l The list of image descriptors.
     */
    public void setImageList(List l) {
        imageList = l;
    }


    /**
     * Override the base class method to return the list of times we created.
     *
     * @return The list of times held by this data source.
     */
    public List doMakeDateTimes() {
        imageTimes = new ArrayList();
        for (Iterator iter = imageList.iterator(); iter.hasNext(); ) {
            Object              object = iter.next();
            AddeImageDescriptor aid    = getDescriptor(object);
            if ( !aid.getIsRelative()) {
                DateTime imageTime = aid.getImageTime();
                if (imageTime != null) {
                    imageTimes.add(imageTime);
                }
            } else {
                imageTimes.add(getRelativeTimeObject(aid));
            }
        }
        return imageTimes;
    }


    /**
     * Initialize the {@link ucar.unidata.data.DataCategory} objects that
     * this data source uses.
     */
    private void makeCategories() {
        twoDTimeSeriesCategories =
            DataCategory.parseCategories("IMAGE-2D-TIME;", false);
        twoDCategories = DataCategory.parseCategories("IMAGE-2D;", false);
        bandCategories = DataCategory.parseCategories("IMAGE-BAND;", false);
        bandTimeSeriesCategories =
            DataCategory.parseCategories("IMAGE-BAND-TIME;", false);

    }

    /**
     * Return the list of {@link ucar.unidata.data.DataCategory} used for
     * single time step data.
     *
     * @return A list of categories.
     */
    public List getTwoDCategories() {
        if (twoDCategories == null) {
            makeCategories();
        }
        return twoDCategories;
    }

    /**
     * Return the list of {@link ucar.unidata.data.DataCategory} used for
     * multiple time step data.
     *
     * @return A list of categories.
     */

    public List getTwoDTimeSeriesCategories() {
        if (twoDCategories == null) {
            makeCategories();
        }
        return twoDTimeSeriesCategories;
    }



    /**
     * Return the list of {@link ucar.unidata.data.DataCategory} used for
     * @return A list of categories.
     */
    public List getBandCategories() {
        if (bandCategories == null) {
            makeCategories();
        }
        return bandCategories;
    }

    /**
     * Return the list of {@link ucar.unidata.data.DataCategory} used for
     * multiple time step data with band information.
     *
     * @return A list of categories.
     */

    public List getBandTimeSeriesCategories() {
        if (bandTimeSeriesCategories == null) {
            makeCategories();
        }
        return bandTimeSeriesCategories;
    }




    /**
     * Create the set of {@link ucar.unidata.data.DataChoice} that represent
     * the data held by this data source.  We create one top-level
     * {@link ucar.unidata.data.CompositeDataChoice} that represents
     * all of the image time steps. We create a set of children
     * {@link ucar.unidata.data.DirectDataChoice}, one for each time step.
     */
    public void doMakeDataChoices() {
        String type = (String) getProperty(PROP_IMAGETYPE, TYPE_SATELLITE);
        List bandInfos =
            (List) getProperty(PROP_BANDINFO, (Object) null);
        Hashtable props = Misc.newHashtable(DataChoice.PROP_ICON,
                                            (type.equals(TYPE_RADAR)
                                             ? "/auxdata/ui/icons/Radar.gif"
                                             : "/auxdata/ui/icons/Satellite.gif"));
        List categories = (imageList.size() > 1)
                          ? getTwoDTimeSeriesCategories()
                          : getTwoDCategories();

        // This is historical an is not added into the list of choices
        // for selection by the users.
        myCompositeDataChoice = new CompositeDataChoice(this, imageList,
                getName(), getDataName(), categories, props);
        myCompositeDataChoice.setUseDataSourceToFindTimes(true);
        doMakeDataChoices(myCompositeDataChoice);

        if ((bandInfos != null) && !bandInfos.isEmpty()) {
            List biCategories = (imageList.size() > 1)
                                ? getBandTimeSeriesCategories()
                                : getBandCategories();
            /*
            if (bandInfos.size() == 1) {
                BandInfo test  = (BandInfo) bandInfos.get(0);
                List     units = test.getCalibrationUnits();
                if ((units == null) || units.isEmpty()
                        || (units.size() == 1)) {
                    return;
                }
            }
            */
            for (Iterator i = bandInfos.iterator(); i.hasNext(); ) {
                BandInfo bi          = i.next();
                String   name        = makeBandParam(bi);
                String   catName     = bi.getBandDescription();
                List biSubCategories = Misc.newList(new DataCategory(catName,
                                           true));
                biSubCategories.addAll(biCategories);
                List l = bi.getCalibrationUnits();
                if (l.isEmpty() || (l.size() == 1)) {
                    DataChoice choice = new DirectDataChoice(this, bi, name,
                                            bi.getBandDescription(),
                                            biCategories, props);
                    addDataChoice(choice);
                } else {
                    for (int j = 0; j < l.size(); j++) {
                        Object   o           = l.get(j);
                        BandInfo bi2         = new BandInfo(bi);
                        String   calUnit     = o.toString();
                        String   calibration = TwoFacedObject.getIdString(o);
                        bi2.setPreferredUnit(calibration);
                        name = makeBandParam(bi2);
                        DataChoice subChoice = new DirectDataChoice(this,
        /**
         * CTOR
         *
                                                   bi2, name, calUnit,
                                                   biSubCategories, props);
                        addDataChoice(subChoice);
                    }
                }
            }
        } else {
            addDataChoice(myCompositeDataChoice);
        }
    }

    /**
     * Make a parmeter name for the BandInfo
     *
     * @param bi    the BandInfo in question
     *
     * @return  a name for the parameter
     */
    private String makeBandParam(BandInfo bi) {
        StringBuffer buf = new StringBuffer();
        buf.append(bi.getSensor());
        buf.append("_Band");
        buf.append(bi.getBandNumber());
        buf.append("_");
        buf.append(bi.getPreferredUnit());
        return buf.toString();
    }

    /**
     * Make the data choices and add them to the given composite
     *
     * @param composite The parent data choice to add to
     */
    private void doMakeDataChoices(CompositeDataChoice composite) {
        int cnt = 0;
        imageTimes = new ArrayList();
        List timeChoices = new ArrayList();
        myDataChoices = new ArrayList();
        String    type  = (String) getProperty(PROP_IMAGETYPE, TYPE_SATELLITE);
        Hashtable props = Misc.newHashtable(DataChoice.PROP_ICON,
                                            (type.equals(TYPE_RADAR)
                                             ? "/auxdata/ui/icons/clock.gif"
                                             : "/auxdata/ui/icons/clock.gif"));



        for (Iterator iter = imageList.iterator(); iter.hasNext(); ) {
            Object              object     = iter.next();
            AddeImageDescriptor aid        = getDescriptor(object);
            String              name       = aid.toString();
            DataSelection       timeSelect = null;
            if ( !aid.getIsRelative()) {
                DateTime imageTime = aid.getImageTime();
                if (imageTime != null) {
                    imageTimes.add(imageTime);
                    //timeSelect = new DataSelection (Misc.newList (imageTime));
                    //We will create the  data choice with an index, not with the actual time.
                    timeSelect =
                        new DataSelection(Misc.newList(new Integer(cnt)));
                }
            } else {
                imageTimes.add(getRelativeTimeObject(aid));
            }
            timeSelect = null;
            DataChoice choice = new DirectDataChoice(this,
                                    new ImageDataInfo(cnt, aid),
                                    composite.getName(), name,
                                    getTwoDCategories(), timeSelect, props);
            myDataChoices.add(choice);
            cnt++;
            timeChoices.add(choice);
        }
        //Sort the data choices.
        composite.replaceDataChoices(sortChoices(timeChoices));
    }



    /**
     * Class ImageDataInfo Holds an index and an AddeImageDescriptor
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.76 $
     */
    public static class ImageDataInfo {

        /** The index */
        private int index;

        /** The AID */
        private AddeImageDescriptor aid;



        /**
         * Ctor for xml encoding
         */
        public ImageDataInfo() {}

         * @param index The index
         * @param aid The aid
         */
        public ImageDataInfo(int index, AddeImageDescriptor aid) {
            this.index = index;
            this.aid   = aid;
        }

        /**
         * Get the index
         *
         * @return The index
         */
        public int getIndex() {
            return index;
        }

        /**
         * Set the index
         *
         * @param v The index
         */
        public void setIndex(int v) {
            index = v;
        }

        /**
         * Get the descriptor
         *
         * @return The descriptor
         */
        public AddeImageDescriptor getAid() {
            return aid;
        }

        /**
         * Set the descriptor
         *
         * @param v The descriptor
         */
        public void setAid(AddeImageDescriptor v) {
            aid = v;
        }

        /**
         * toString
         *
         * @return toString
         */
        public String toString() {
            return "index:" + index + " " + aid;
        }

    }



    /**
     * Create the actual data represented by the given
     * {@link ucar.unidata.data.DataChoice}.
     *
     * @param dataChoice        Either the
     *                          {@link ucar.unidata.data.CompositeDataChoice}
     *                          representing all time steps or a
     *                          {@link ucar.unidata.data.DirectDataChoice}
     *                          representing a single time step.
     * @param category          Not really used.
     * @param dataSelection     Defines any time subsets.
     * @param requestProperties extra request properties
     *
     * @return The image or image sequence data.
     *
     * @throws RemoteException    Java RMI problem
     * @throws VisADException     VisAD problem
     */
    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
                                DataSelection dataSelection,
                                Hashtable requestProperties)
            throws VisADException, RemoteException {

        try {
            if (dataChoice instanceof CompositeDataChoice) {
                return makeImageSequence(myCompositeDataChoice,
                                         dataSelection);
            } else if (hasBandInfo(dataChoice)) {
                return makeImageSequence(dataChoice, dataSelection);
            }

        } finally {
            //      System.err.println ("ImageDataSource.getDataInner:done");
        }

        return (Data) makeImage(dataChoice, dataSelection);
    }


    /**
     * Override the base class method for the non composite choices.
     *
     * @param dataChoice          Either the
     *                            {@link ucar.unidata.data.CompositeDataChoice}
     *                            representing all time steps or a
     *                            {@link ucar.unidata.data.DirectDataChoice}
     *                            representing a single time step.
     *  @return The list of times represented by the given dataChoice.
     */
    public List getAllDateTimes(DataChoice dataChoice) {
        if ((dataChoice instanceof CompositeDataChoice)
                || hasBandInfo(dataChoice)) {
            return super.getAllDateTimes(dataChoice);
        }
        Object dttmObject = getDateTime(dataChoice);
        if (dttmObject != null) {
            return Misc.newList(dttmObject);
        }
        return new ArrayList();
    }

    /**
     * Override the base class method for the non-composite choices.
     *
     * @param dataChoice         Either the
     *                           {@link ucar.unidata.data.CompositeDataChoice}
     *                           representing all time steps or a
     *                           {@link ucar.unidata.data.DirectDataChoice}
     *                           representing a single time step.
     * @return The list of times represented by the given dataChoice.
     */
    public List getSelectedDateTimes(DataChoice dataChoice) {
        if ((dataChoice instanceof CompositeDataChoice)
                || hasBandInfo(dataChoice)) {
            return super.getSelectedDateTimes();
        }
        Object dttmObject = getDateTime(dataChoice);
        if (dttmObject != null) {
            return Misc.newList(dttmObject);
        }
        return new ArrayList();
    }


    /**
     * Utility method to get the time associated with the given dataChoice.
     *
     * @param dataChoice     choice for selection
     * @return  the associated time
     */
    private Object getDateTime(DataChoice dataChoice) {
        Object              id  = dataChoice.getId();
        AddeImageDescriptor aid = getDescriptor(id);
        if (aid.getIsRelative()) {
            return getRelativeTimeObject(aid);
        } else {
            return aid.getImageTime();
        }
    }


    /**
     * Get the object that we use to display relative time. Relative time is defined
     * using an integer index, 0...n. We don't want to show the actual integer.
     * Rather we want to show "Third most recent", "Fourth most recent", etc.
     *
     * @param aid The image descriptor
     * @return The object that represents the relative time index of the aid
     */
    private Object getRelativeTimeObject(AddeImageDescriptor aid) {
        return new TwoFacedObject(aid.toString(),
                                  new Integer(aid.getRelativeIndex()));
    }


    /**
     * Create the single image defined by the given dataChoice.
     *
     * @param dataChoice      The choice.
     * @param subset          any time subsets.
     *
     * @return The data.
     *
     * @throws RemoteException    Java RMI problem
     * @throws VisADException     VisAD problem
     */
    protected final SingleBandedImage makeImage(DataChoice dataChoice,
            DataSelection subset)
            throws VisADException, RemoteException {

        AddeImageDescriptor aid = getDescriptor(dataChoice.getId());
        if (aid == null) {
            return null;
        }
        DateTime dttm = aid.getImageTime();
        if ((subset != null) && (dttm != null)) {
            List times = getTimesFromDataSelection(subset, dataChoice);
            if ((times != null) && (times.indexOf(dttm) == -1)) {
                return null;
            }
        }
        return makeImage(aid, null, false, "");
    }



    /**
     * Create the single image defined by the given dataChoice.
     *
     * @param aid AddeImageDescriptor
     * @param rangeType This is the sample rangeType. For the first image this is null and this method will immediately read the data
     * @param fromSequence from a sequence
     * @param readLabel  the label
     *
     * @return The data.
     *
     * @throws RemoteException    Java RMI problem
     * @throws VisADException     VisAD problem
     */
    private SingleBandedImage makeImage(AddeImageDescriptor aid,
                                        MathType rangeType,
                                        boolean fromSequence,
                                        String readLabel)
            throws VisADException, RemoteException {

        if (aid == null) {
            return null;
        }
        String            source = aid.getSource();

        SingleBandedImage result = (SingleBandedImage) getCache(source);
        if (result != null) {
            return result;
        }

        try {
            AddeImageInfo aii     = aid.getImageInfo();
            AreaDirectory areaDir = null;
            try {
                if (aii != null) {
                    if (currentDirs != null) {
                        int    pos        = Math.abs(aii.getDatasetPosition());
                        int    band       = 0;
                        String bandString = aii.getBand();
                        if ((bandString != null)
                                && !bandString.equals(aii.ALL)) {
                            band = new Integer(bandString).intValue();
                        }
                        //TODO: even though the band is non-zero we might only get back one band
                        band    = 0;
                        areaDir =
                            currentDirs[currentDirs.length - pos - 1][band];
                    } else {
                        //If its absolute time then just use the AD from the descriptor
                        if ((aii.getStartDate() != null)
                                || (aii.getEndDate() != null)) {
                            areaDir = aid.getDirectory();
                            //                                System.err.println("absolute time:" + areaDir.getNominalTime());
                            //System.err.println(" from aii:" +aii.getStartDate());
                        } else {
                            //                            System.err.println(
                            //                                "relative time without currentDirs "
                            //                                + fromSequence);
                        }
                    }
                }
            } catch (Exception exc) {
                LogUtil.printMessage("error looking up area dir");
                exc.printStackTrace();
                return null;
            }

            if (areaDir == null) {
                areaDir = aid.getDirectory();
            }

            if ( !getCacheDataToDisk()) {
                areaDir = null;
            }
            if ( !fromSequence
                    || (aid.getIsRelative() && (currentDirs == null))) {
                areaDir = null;
            }

            if (areaDir != null) {
                int hash = ((aii != null)
                            ? aii.makeAddeUrl().hashCode()
                            : areaDir.hashCode());

                //If the range type is null then we are reading the first image
                //and we want to read it immediately so we can get the correct range
                //from the data itself
                if (rangeType == null) {
                    result = AreaImageFlatField.createImmediate(aid,
                            readLabel);
                } else {
                    //Else, pass in the already created range type
                    result = AreaImageFlatField.create(aid, areaDir,
                            rangeType, readLabel);
                }
            } else {
                AreaAdapter aa = new AreaAdapter(aid.getSource(), false);
                timeMap.put(aid.getSource(), aa.getImageStartTime());
                result = aa.getImage();
                aa     = null;
            }
            putCache(source, result);
            return result;
        } catch (java.io.IOException ioe) {
            throw new VisADException("Creating AreaAdapter - " + ioe);
        }

    }

    /**
     * Reload the data
     */
    public void reloadData() {
        currentDirs = null;
        super.reloadData();
    }


    /**
     * Get whether we should cache to disk
     *
     * @return  true
     */
    public boolean getCacheDataToDisk() {
        return true;
    }

    /** the ranget type */
    private MathType rangeType = null;

    /**
     * Create the  image sequence defined by the given dataChoice.
     *
     * @param dataChoice     The choice.
     * @param subset     any time subsets.
     * @return The image sequence.
     *
     * @throws RemoteException    Java RMI problem
     * @throws VisADException     VisAD problem
     */
    protected ImageSequence makeImageSequence(DataChoice dataChoice,
            DataSelection subset)
            throws VisADException, RemoteException {

        try {
            List descriptorsToUse = new ArrayList();
            if (hasBandInfo(dataChoice)) {
                descriptorsToUse = getDescriptors(dataChoice, subset);
            } else {
                List choices = (dataChoice instanceof CompositeDataChoice)
                               ? getChoicesFromSubset(
                                   (CompositeDataChoice) dataChoice, subset)
                               : Arrays.asList(new DataChoice[] {
                                   dataChoice });
                for (Iterator iter = choices.iterator(); iter.hasNext(); ) {
                    DataChoice          subChoice = (DataChoice) iter.next();
                    AddeImageDescriptor aid       =
                        getDescriptor(subChoice.getId());
                    if (aid == null) {
                        continue;
                    }
                    DateTime dttm = aid.getImageTime();
                    if ((subset != null) && (dttm != null)) {
                        List times = getTimesFromDataSelection(subset,
                                         dataChoice);
                        if ((times != null) && (times.indexOf(dttm) == -1)) {
                            continue;
                        }
                    }
                    descriptorsToUse.add(aid);
                }
            }

            if (descriptorsToUse.size() == 0) {
                return null;
            }
            AddeImageInfo biggestPosition = null;
            int           pos             = 0;
            boolean       anyRelative     = false;
            //Find the descriptor with the largest position
            String biggestSource = null;
            for (Iterator iter =
                    descriptorsToUse.iterator(); iter.hasNext(); ) {
                AddeImageDescriptor aid = (AddeImageDescriptor) iter.next();
                if (aid.getIsRelative()) {
                    anyRelative = true;
                }
                AddeImageInfo aii = aid.getImageInfo();

                //Are we dealing with area files here?
                if (aii == null) {
                    break;
                }

                //                System.err.println (" aii:" +aii.makeAddeUrl());
                //                System.err.println (" aid:" + aid.getSource());
                //Check if this is absolute time
                if ((aii.getStartDate() != null)
                        || (aii.getEndDate() != null)) {
                    biggestPosition = null;
                    break;
                }
                if ((biggestPosition == null)
                        || (Math.abs(aii.getDatasetPosition()) > pos)) {
                    pos             = Math.abs(aii.getDatasetPosition());
                    biggestPosition = aii;
                    biggestSource   = aid.getSource();
                }
            }

            //            System.err.println(getCacheDataToDisk() + " " + biggestPosition);

            //If any of them are relative then read in the list of AreaDirectorys so we can get the correct absolute times
            // TODO:  revisit this

            if (getCacheDataToDisk() && anyRelative
                    && (biggestPosition != null)) {
                biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDIR);
                /*
                  System.err.println(biggestPosition.makeAddeUrl()
                  + "\nfrom aid:" + biggestSource);
                  System.err.println(biggestPosition.makeAddeUrl()
                  + "\nfrom aii:" + biggestPosition.makeAddeUrl());
                */
                AreaDirectoryList adl =
                    new AreaDirectoryList(biggestPosition.makeAddeUrl());
                biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDATA);
                currentDirs = adl.getSortedDirs();
            } else {
                currentDirs = null;
            }

            ThreadManager threadManager =
                new ThreadManager("image data reading");
            //threadManager.debug = true;
            final ImageSequenceManager sequenceManager =
                new ImageSequenceManager();
            int                           cnt    = 1;
            DataChoice                    parent = dataChoice.getParent();
            final List images =
                new ArrayList();
            rangeType = null;
            for (Iterator iter =
                    descriptorsToUse.iterator(); iter.hasNext(); ) {
                final AddeImageDescriptor aid =
                    (AddeImageDescriptor) iter.next();
                if (currentDirs != null) {
                    int idx =
                        Math.abs(aid.getImageInfo().getDatasetPosition());
                    if (idx >= currentDirs.length) {
                        //                        System.err.println("skipping index:" + idx);
                        continue;
                    }
                }

                String label = "";
                if (parent != null) {
                    label = label + parent.toString() + " ";
                } else {
                    DataCategory displayCategory =
                        dataChoice.getDisplayCategory();
                    if (displayCategory != null) {
                        label = label + displayCategory + " ";
                    }
                }
                label = label + dataChoice.toString();
                final String readLabel = "Time: " + (cnt++) + "/"
                                         + descriptorsToUse.size() + "  "
                                         + label;

                if (rangeType == null) {
                    try {

                        SingleBandedImage image = makeImage(aid, rangeType,
                                                      true, readLabel);
                        if (image != null) {
                            //This is the first one, so grab its rangeType to use for later images
                            rangeType =
                                ((FunctionType) image.getType()).getRange();
                            synchronized (images) {
                                images.add(image);
                            }
                        }

                    } catch (VisADException ve) {
                        // this is a nested error so just print out the real thing
                        String realError = ve.getMessage();
                        realError =
                            realError.substring(realError.lastIndexOf(":")
                                + 1);
                        LogUtil.printMessage(realError);
                        //return null;
                    }
                } else {  // have the rangeType so put reading the rest in threads
                    threadManager.addRunnable(new ThreadManager.MyRunnable() {
                        public void run() throws Exception {
                            try {

                                SingleBandedImage image = makeImage(aid,
                                                              rangeType,
                                                              true,
                                                              readLabel);
                                if (image != null) {
                                    synchronized (images) {
                                        images.add(image);
                                    }
                                }

                            } catch (VisADException ve) {
                                // this is a nested error so just print out the real thing
                                String realError = ve.getMessage();
                                realError = realError.substring(
                                    realError.lastIndexOf(":") + 1);
                                LogUtil.printMessage(realError);
                                //return null;
                            }
                        }
                    });

                }
            }

            try {
                threadManager.runInParallel(
                    getDataContext().getIdv().getMaxDataThreadCount());
            } catch (VisADException ve) {
                LogUtil.printMessage(ve.toString());
            }
            if (images.isEmpty()) {
                return null;
            }

            TreeMap imageMap = new TreeMap();
            for (SingleBandedImage image : images) {
                imageMap.put(image.getStartTime(), image);
            }
            List sortedImages =
                (List) new ArrayList(imageMap.values());
            if ((sortedImages.size() > 0)
                    && (sortedImages.get(0) instanceof AreaImageFlatField)) {
                DataRange[] sampleRanges = null;
                Set         domainSet    = null;
                for (SingleBandedImage sbi : sortedImages) {
                    AreaImageFlatField aiff = (AreaImageFlatField) sbi;
                    sampleRanges = aiff.getRanges(true);
                    if (domainSet == null) {
                        domainSet = aiff.getDomainSet();
                    }
                    if ((sampleRanges != null) && (sampleRanges.length > 0)) {
                        for (int rangeIdx = 0; rangeIdx < sampleRanges.length;
                                rangeIdx++) {
                            DataRange r = sampleRanges[rangeIdx];
                            if (Double.isInfinite(r.getMin())
                                    || Double.isInfinite(r.getMax())) {
                                sampleRanges = null;
                                break;
                            }
                        }
                    }
                    if (sampleRanges != null) {
                        break;
                    }
                }

                if (sampleRanges != null) {
                    for (SingleBandedImage sbi : sortedImages) {
                        AreaImageFlatField aiff = (AreaImageFlatField) sbi;
                        aiff.setSampleRanges(sampleRanges);
                        aiff.setDomainIfNeeded(domainSet);
                    }
                }
            }

            SingleBandedImage[] imageArray =
                (SingleBandedImage[]) sortedImages.toArray(
                    new SingleBandedImage[sortedImages.size()]);
            FunctionType imageFunction =
                (FunctionType) imageArray[0].getType();
            FunctionType ftype = new FunctionType(RealType.Time,
                                     imageFunction);
            return new ImageSequenceImpl(ftype, imageArray);
        } catch (Exception exc) {
            throw new ucar.unidata.util.WrapperException(exc);
        }

    }



    /**
     * Get a list of descriptors from the choice and subset
     *
     * @param dataChoice  Data choice
     * @param subset  subsetting info
     *
     * @return  list of descriptors matching the selection
     */
    private List getDescriptors(DataChoice dataChoice, DataSelection subset) {

        List    times = getTimesFromDataSelection(subset, dataChoice);
        boolean usingTimeDriver = ((subset != null)
                                   && (subset.getTimeDriverTimes() != null));
        if (usingTimeDriver) {
            times = subset.getTimeDriverTimes();
        }
        if ((times == null) || times.isEmpty()) {
            times = imageTimes;
        }

        List descriptors = new ArrayList();
        if (usingTimeDriver) {
            AddeImageDescriptor aid = getDescriptor(imageList.get(0));
            if (aid.getImageInfo() != null) {
                try {
                    AddeImageInfo aii =
                        (AddeImageInfo) aid.getImageInfo().clone();
                    // set the start and end dates
                    Collections.sort(times);
                    DateTime start = (DateTime) times.get(0);
                    DateTime end   = (DateTime) times.get(times.size() - 1);
                    // In ADDE, you can't specify something like DAY=2011256 2011257 TIME=23:45:00 01:45:00
                    // and expect that to be 2011256/23:45 to 2011257 01:45.  Time ranges are on a per day
                    // basis.  So, we see if the starting time is a different day than the ending day and if so,
                    // we set the start time to be 00Z on the first day an 23:59Z on the end day.
                    // Even worse is that for archive datasets, you can't span multiple days.  So make separate
                    // requests for each day.
                    String       startDay = UtcDate.getYMD(start);
                    String       endDay   = UtcDate.getYMD(end);
                    List days     = new ArrayList();
                    if ( !startDay.equals(endDay)) {
                        days = getUniqueDayStrings(times);
                    } else {
                        days.add(startDay);
                    }
                    HashMap dateDir =
                        new HashMap();
                    List dirTimes = new ArrayList();
                    for (String day : days) {
                        startDay = day + " 00:00:00";
                        endDay   = day + " 23:59:59";
                        start    = DateTime.createDateTime(startDay,
                                DateTime.DEFAULT_TIME_FORMAT);
                        end = DateTime.createDateTime(endDay,
                                DateTime.DEFAULT_TIME_FORMAT);
                        aii.setStartDate(new Date((long) (start
                            .getValue(CommonUnit
                                .secondsSinceTheEpoch) * 1000)));
                        aii.setEndDate(new Date((long) (end
                            .getValue(CommonUnit
                                .secondsSinceTheEpoch) * 1000)));
                        // make the request for the times (AreaDirectoryList)
                        aii.setRequestType(aii.REQ_IMAGEDIR);
                        AreaDirectoryList ad;
                        try {  // we may be asking for a date that doesn't exist
                            ad = new AreaDirectoryList(aii.getURLString());
                        } catch (AreaFileException afe) {
                            // TODO: This is a hack because different servers return different
                            // messages.  AREA and GINI servers seem to have "no images" in the
                            // exception message when there are no images.
                            if (afe.getMessage().toLowerCase().indexOf(
                                    "no images") >= 0) {
                                continue;
                            } else {
                                throw afe;
                            }
                        }
                        AreaDirectory[][] dirs = ad.getSortedDirs();
                        for (int d = 0; d < dirs.length; d++) {
                            AreaDirectory dir     = dirs[d][0];
                            DateTime      dirTime =
                                new DateTime(dir.getNominalTime());
                            dateDir.put(dirTime, dir);
                            dirTimes.add(dirTime);
                        }
                    }
                    List matchedTimes = selectTimesFromList(subset,
                                                      dirTimes, times);
                    imageList  = new ArrayList();
                    for (DateTime dirTime : matchedTimes) {
                        AreaDirectory dir = dateDir.get(dirTime);
                        // shouldn't happen, but what the hey
                        if (dir == null) {
                            continue;
                        }
                        AddeImageInfo newaii =
                            (AddeImageInfo) aid.getImageInfo().clone();
                        newaii.setRequestType(aii.REQ_IMAGEDATA);
                        newaii.setStartDate(dir.getNominalTime());
                        newaii.setEndDate(dir.getNominalTime());
                        setBandInfo(dataChoice, newaii);
                        AddeImageDescriptor newaid =
                            new AddeImageDescriptor(dir,
                                newaii.getURLString(), newaii);
                        newaid.setIsRelative(false);
                        descriptors.add(newaid);
                        imageList.add(newaid);
                    }
                } catch (CloneNotSupportedException cnse) {
                    System.out.println("unable to clone aii");
                } catch (VisADException vader) {
                    System.out.println("unable to get date values");
                } catch (AreaFileException afe) {
                    System.out.println("unable to make request");
                } catch (Exception excp) {
                    System.out.println("Got an exception: "
                                       + excp.getMessage());
                }
                return descriptors;
            } else if (imageList != null) {
                return imageList;
            }
        }
        for (Iterator iter = times.iterator(); iter.hasNext(); ) {
            Object              time  = iter.next();
            AddeImageDescriptor found = null;
            for (Iterator iter2 = imageList.iterator(); iter2.hasNext(); ) {
                AddeImageDescriptor aid = getDescriptor(iter2.next());
                if (aid != null) {
                    if (aid.getIsRelative()) {
                        Object id = (time instanceof TwoFacedObject)
                                    ? ((TwoFacedObject) time).getId()
                                    : time;
                        if ((id instanceof Integer)
                                && ((Integer) id).intValue()
                                   == aid.getRelativeIndex()) {
                            found = aid;
                            break;
                        }

                    } else {
                        if (aid.getImageTime().equals(time)) {
                            found = aid;
                            break;
                        }
                    }

                }
            }
            if (found != null) {
                try {
                    AddeImageDescriptor desc = new AddeImageDescriptor(found);
                    //Sometimes we might have a null imageinfo
                    if (desc.getImageInfo() != null) {
                        AddeImageInfo aii =
                            (AddeImageInfo) desc.getImageInfo().clone();
                        setBandInfo(dataChoice, aii);
                        /*
                        BandInfo bi = (BandInfo) dataChoice.getId();
                        List bandInfos =
                            (List) getProperty(PROP_BANDINFO,
                                (Object) null);
                        boolean hasBand = true;
                        //If this data source has been changed after we have create a display
                        //then the possibility exists that the bandinfo contained by the incoming
                        //data choice might not be valid. If it isn't then default to the first
                        //one in the list
                        if (bandInfos != null) {
                            hasBand = bandInfos.contains(bi);
                            if ( !hasBand) {
                                //                                System.err.println("has band = " + bandInfos.contains(bi));
                            }
                            if ( !hasBand && (bandInfos.size() > 0)) {
                                bi = bandInfos.get(0);
                            } else {
                                //Not sure what to do here.
                            }
                        }
                        aii.setBand("" + bi.getBandNumber());
                        aii.setUnit(bi.getPreferredUnit());
                        */
                        desc.setImageInfo(aii);
                        desc.setSource(aii.getURLString());
                    }
                    descriptors.add(desc);
                } catch (CloneNotSupportedException cnse) {}
            }
        }
        return descriptors;

    }

    /**
     * Get a list of unique YMD day strings in the list of times
     *
     * @param times  list of times
     *
     * @return  the list of unique strings
     */
    private List getUniqueDayStrings(List times) {
        List days = new ArrayList();
        for (DateTime time : times) {
            String dateString = UtcDate.getYMD(time);
            if ( !days.contains(dateString)) {
                days.add(dateString);
            }
        }
        return days;
    }


    /**
     * Set the band information on the data choice
     *
     * @param dataChoice  the data choice
     * @param aii  the AddeImageInfo with the image stuff
     */
    private void setBandInfo(DataChoice dataChoice, AddeImageInfo aii) {
        BandInfo       bi        = (BandInfo) dataChoice.getId();
        List bandInfos =
            (List) getProperty(PROP_BANDINFO, (Object) null);
        boolean hasBand = true;
        //If this data source has been changed after we have create a display 
        //then the possibility exists that the bandinfo contained by the incoming
        //data choice might not be valid. If it isn't then default to the first 
        //one in the list
        if (bandInfos != null) {
            hasBand = bandInfos.contains(bi);
            if ( !hasBand) {
                //                                System.err.println("has band = " + bandInfos.contains(bi));
            }
            if ( !hasBand && (bandInfos.size() > 0)) {
                bi = bandInfos.get(0);
            } else {
                //Not sure what to do here.
            }
        }
        aii.setBand("" + bi.getBandNumber());
        aii.setUnit(bi.getPreferredUnit());
    }

    /**
     * Get the subset of the composite based on the selection
     *
     * @param choice  composite choice
     * @param subset  time selection
     *
     * @return subset list
     */
    private List getChoicesFromSubset(CompositeDataChoice choice,
                                      DataSelection subset) {
        List choices = choice.getDataChoices();
        if (subset == null) {
            return choices;
        }
        List times = subset.getTimes();
        if (times == null) {
            return choices;
        }
        times = TwoFacedObject.getIdList(times);
        List   subChoices = new ArrayList();
        Object firstTime  = times.get(0);
        if (firstTime instanceof Integer) {
            for (Iterator iter = times.iterator(); iter.hasNext(); ) {
                subChoices.add(
                    choices.get(((Integer) iter.next()).intValue()));
            }
        } else {  // TODO: what if they are DateTimes?
            subChoices.addAll(choices);
        }
        return subChoices;
    }


    /**
     * Check to see if this ImageDataSource is equal to the object
     * in question.
     *
     * @param o  object in question
     *
     * @return true if they are the same or equivalent objects
     */
    public boolean equals(Object o) {
        if ( !super.equals(o)) {
            return false;
        }
        if ( !getClass().equals(o.getClass())) {
            return false;
        }
        ImageDataSource that = (ImageDataSource) o;
        return ((this == that) || Misc.equals(imageList, that.imageList));
    }

    /**
     * Override the hashCode method. Use name and imageList.
     *
     * @return The hashcode.
     */
    public int hashCode() {
        int hashCode = getName().hashCode();
        hashCode ^= imageList.hashCode();
        return hashCode;
    }



    /**
     * Get the name for the main data object
     *
     * @return name of main data object
     */
    public String getDataName() {
        return "Image Sequence";
    }

    /**
     * Get an expanded description for the details display.  Override
     * base class implementation to add more info.
     *
     * @return full description of this data source
     */
    public String getFullDescription() {
        StringBuffer buf = new StringBuffer(super.getFullDescription());
        buf.append("

"); List images = getImageList(); if (images != null) { for (int i = 0; i < images.size(); i++) { Object o = images.get(i); if (o instanceof AddeImageDescriptor) { AreaDirectory ad = ((AddeImageDescriptor) o).getDirectory(); if (i == 0) { buf.append( * } ""); } buf.append(""); buf.append(""); buf.append(""); buf.append(""); } else { if (i == 0) { buf.append( "
LocationDateSize (Lines X Elements) Band
"); String path = ((AddeImageDescriptor) o).getSource(); if (path.length() > 50) { String tmp = path; path = ""; while (tmp.length() > 50) { if (path.length() > 0) { path = path + "
"; } path = path + tmp.substring(0, 49); tmp = tmp.substring(49); } path = path + "
" + tmp; } buf.append(path); buf.append("
"); buf.append("" + ad.getNominalTime()); buf.append(""); buf.append(ad.getLines()); buf.append(" X "); buf.append(ad.getElements()); buf.append(""); buf.append("Band "); buf.append(ad.getBands()[0]); buf.append("
"); } buf.append(""); } } buf.append("
Location
"); buf.append(o.toString()); buf.append("
"); } return buf.toString(); } /** * If we are polling some directory this method gets called when * there is a new file. We set the file name, clear our state, * reload the metadata and tell listeners of the change. * * @param file new File to use. */ public void newFileFromPolling(File file) { // System.err.println("new file from polling"); initDataFromPollingInfo(); dataChoices = null; getDataChoices(); getDataContext().dataSourceChanged(this); Hashtable cache = CacheManager.findOrCreate(dataCacheKey); flushCache(); //Should be only one here CompositeDataChoice cdc = myCompositeDataChoice; //(CompositeDataChoice) getDataChoices().get(0); cdc.removeAllDataChoices(); doMakeDataChoices(cdc); for (int i = 0; i < imageList.size(); i++) { AddeImageDescriptor aid = getDescriptor(imageList.get(i)); String source = aid.getSource(); Object cachedData = cache.get(source); if (cachedData != null) { //System.err.println("keeping the cache"); putCache(source, cachedData); } } notifyDataChange(); } /** * Sort the list of data choices on their time * * @param choices The data choices * * @return The data choices sorted */ private List sortChoices(List choices) { Object[] choicesArray = choices.toArray(); Comparator comp = new Comparator() { public int compare(Object o1, Object o2) { AddeImageDescriptor aid1 = getDescriptor(o1); * * AddeImageDescriptor aid2 = getDescriptor(o2); if ((aid1 == null) || (aid2 == null)) { return -1; } if (aid1.getIsRelative()) { if (aid1.getRelativeIndex() < aid2.getRelativeIndex()) { return 0; } else if (aid1.getRelativeIndex() == aid2.getRelativeIndex()) { return 1; } return -1; } return aid1.getImageTime().compareTo(aid2.getImageTime()); } }; Arrays.sort(choicesArray, comp); return new ArrayList(Arrays.asList(choicesArray)); } /** * Check if the DataChoice has a BandInfo for it's Id * * @param dataChoice choice to check * * @return true if the choice ID is a BandInfo */ private boolean hasBandInfo(DataChoice dataChoice) { return dataChoice.getId() instanceof BandInfo; } /** * Called when Datasource is removed. */ public void doRemove() { super.doRemove(); myDataChoices = null; myCompositeDataChoice = null; imageTimes = null; currentDirs = null; } } ======= /* * Copyright 1997-2012 Unidata Program Center/University Corporation for * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307, * support@unidata.ucar.edu. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package ucar.unidata.data.imagery; import edu.wisc.ssec.mcidas.AreaDirectory; import edu.wisc.ssec.mcidas.AreaDirectoryList; import edu.wisc.ssec.mcidas.AreaFileException; import ucar.unidata.data.CompositeDataChoice; import ucar.unidata.data.DataCategory; import ucar.unidata.data.DataChoice; import ucar.unidata.data.DataSelection; import ucar.unidata.data.DataSourceDescriptor; import ucar.unidata.data.DataSourceImpl; import ucar.unidata.data.DirectDataChoice; import ucar.unidata.util.CacheManager; import ucar.unidata.util.IOUtil; import ucar.unidata.util.LogUtil; import ucar.unidata.util.Misc; import ucar.unidata.util.PollingInfo; import ucar.unidata.util.StringUtil; import ucar.unidata.util.TwoFacedObject; import ucar.visad.UtcDate; import ucar.visad.data.AreaImageFlatField; import visad.CommonUnit; import visad.Data; import visad.DateTime; import visad.FunctionType; import visad.MathType; import visad.RealType; import visad.Set; import visad.VisADException; import visad.data.DataRange; import visad.data.mcidas.AreaAdapter; import visad.meteorology.ImageSequence; import visad.meteorology.ImageSequenceImpl; import visad.meteorology.ImageSequenceManager; import visad.meteorology.SingleBandedImage; import visad.util.ThreadManager; import java.io.File; import java.rmi.RemoteException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.TimeZone; import java.util.TreeMap; /** * Abstract DataSource class for images files. * * @author IDV development team */ public abstract class ImageDataSource extends DataSourceImpl { /** Type of image, radar or satellite. Set by the chooser to disambiguate between types */ public static final String PROP_IMAGETYPE = "prop.imagetype"; /** radar type */ public static final String TYPE_RADAR = "radar"; /** satellite type */ public static final String TYPE_SATELLITE = "satellite"; /** satellite type */ public static final String PROP_BANDINFO = "bandinfo"; /** list of twod categories */ private List twoDCategories; /** list of 2D time series categories */ private List twoDTimeSeriesCategories; /** list of twod categories */ private List bandCategories; /** list of 2D time series categories */ private List bandTimeSeriesCategories; /** list of images */ protected List imageList; /** list of image times */ protected List imageTimes = new ArrayList(); /** My composite */ private CompositeDataChoice myCompositeDataChoice; /** children choices */ private List myDataChoices = new ArrayList(); /** current directories */ private AreaDirectory[][] currentDirs; /** timeMap */ private Hashtable timeMap = new Hashtable(); /** * The parameterless constructor for unpersisting. */ public ImageDataSource() {} /** * Create a new ImageDataSource with a list of (String) images. These * can either be AREA files or ADDE URLs. * * @param descriptor The descriptor for this data source. * @param images Array of file anmes or urls. * @param properties The properties for this data source. */ public ImageDataSource(DataSourceDescriptor descriptor, String[] images, Hashtable properties) { super(descriptor, "Image data set", "Image data source", properties); if ( !initDataFromPollingInfo()) { setImageList(makeImageDescriptors(images)); } setDescription(getImageDataSourceName()); } /** * Create a new ImageDataSource with a list of (String) images. These * can either be AREA files or ADDE URLs. * @return Directory to poll on. * @param descriptor The descriptor for this data source. * @param images Array of file anmes or urls. * @param properties The properties for this data source. */ public ImageDataSource(DataSourceDescriptor descriptor, List images, Hashtable properties) { this(descriptor, StringUtil.listToStringArray(images), properties); } /** * Create a new ImageDataSource with the given {@link ImageDataset}. * The dataset may hold eight AREA file filepaths or ADDE URLs. * * @param descriptor The descriptor for this data source. * @param ids The dataset. * @param properties The properties for this data source. */ public ImageDataSource(DataSourceDescriptor descriptor, ImageDataset ids, Hashtable properties) { super(descriptor, ids.getDatasetName(), "Image data source", properties); setImageList(new ArrayList(ids.getImageDescriptors())); setDescription(getImageDataSourceName()); } /** * Reload data * * @param object the object to reload * @param properties the properties */ public void reloadData(Object object, Hashtable properties) { if (object instanceof ImageDataset) { ImageDataset ids = (ImageDataset) object; setImageList(new ArrayList(ids.getImageDescriptors())); } else if (object instanceof List) { String[] images = StringUtil.listToStringArray((List) object); setImageList(makeImageDescriptors(images)); } else { try { String[] images = (String[]) object; setImageList(makeImageDescriptors(images)); } catch (Exception exc) { return; } } setDescription(getImageDataSourceName()); reloadProperties(properties); reloadData(); } /** * Handle when this data source gets new files to use at runtime (e.g., from isl) * * @param files List of files * */ public void setNewFiles(List files) { setImageList( makeImageDescriptors(StringUtil.listToStringArray(files))); } /** * Update the state * * @param newObject the new object * @param newProperties the new properties */ public void updateState(Object newObject, Hashtable newProperties) { super.updateState(newObject, newProperties); if (newObject instanceof ImageDataset) { ImageDataset ids = (ImageDataset) newObject; setImageList(new ArrayList(ids.getImageDescriptors())); setDescription(getImageDataSourceName()); } else if (newObject instanceof List) { setTmpPaths((List) newObject); } else if (newObject instanceof String) { setTmpPaths(Misc.newList(newObject)); } } /** * Get the paths for saving data files * * @return data paths */ public List getDataPaths() { List paths = new ArrayList(); SimpleDateFormat sdf = new SimpleDateFormat("_" + DATAPATH_DATE_FORMAT); try { */ for (int i = 0; i < imageList.size(); i++) { AddeImageDescriptor aid = getDescriptor(imageList.get(i)); String path = aid.getSource(); DateTime dttm = (DateTime) timeMap.get(path); /* if(dttm!=null) { String dateString = sdf.format(ucar.visad.Util.makeDate(dttm)); if(path.indexOf(".area")>=0 && path.indexOf(dateString)==-1) { path = path.replace(".area", dateString+".area"); } System.err.println("path:" + path); paths.add(new Object[]{path,path+dateString}); } else {*/ paths.add(path); // } } } catch (Exception exc) { throw new ucar.unidata.util.WrapperException(exc); } return paths; } /** * Override the init method for when this data source is unpersisted. * We simply check the imageList to see if this object came from a * legacy bundle. */ public void initAfterUnpersistence() { super.initAfterUnpersistence(); List tmp = getTmpPaths(); if (tmp != null) { imageList = new ArrayList(); for (int i = 0; i < tmp.size(); i++) { imageList.add(new AddeImageDescriptor(tmp.get(i).toString())); } } if ((imageList != null) && (imageList.size() > 0) && (imageList.get(0) instanceof String)) { List tmpList = imageList; imageList = new ArrayList(); for (int i = 0; i < tmpList.size(); i++) { imageList.add( new AddeImageDescriptor(tmpList.get(i).toString())); } } initDataFromPollingInfo(); } /** * Can this data source cache its * * @return can cache data to disk */ public boolean canCacheDataToDisk() { return true; } /** * Is this data source capable of saving its data to local disk * * @return Can save to local disk */ public boolean canSaveDataToLocalDisk() { if (isFileBased()) { return false; } List bandInfos = (List) getProperty(PROP_BANDINFO, (Object) null); if ((bandInfos == null) || (bandInfos.size() == 0)) { return true; } if (bandInfos.size() > 1) { return false; } List l = bandInfos.get(0).getCalibrationUnits(); if (l.size() > 1) { return false; } return true; } /** * Save files to local disk * * @param prefix destination dir and file prefix * @param loadId For JobManager * @param changeLinks Change internal file references * * @return Files copied * * @throws Exception On badness */ protected List saveDataToLocalDisk(String prefix, Object loadId, boolean changeLinks) throws Exception { List urls = new ArrayList(); List suffixes = new ArrayList(); * SimpleDateFormat sdf = new SimpleDateFormat("_" + DATAPATH_DATE_FORMAT); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); for (int i = 0; i < imageList.size(); i++) { AddeImageDescriptor aid = getDescriptor(imageList.get(i)); String url = aid.getSource(); DateTime dttm = (DateTime) timeMap.get(url); if (dttm != null) { suffixes.add(sdf.format(ucar.visad.Util.makeDate(dttm)) + ".area"); } else { suffixes.add(i + ".area"); } urls.add(url); } List newFiles = IOUtil.writeTo(urls, prefix, suffixes, loadId); // System.err.println ("files:" + newFiles); if (newFiles == null) { return null; } if (changeLinks) { imageList = newFiles; } return newFiles; } /** * Method for intializing the data. * * * @return result */ protected boolean initDataFromPollingInfo() { PollingInfo pollingInfo = getPollingInfo(); if ( !pollingInfo.getForFiles() || !pollingInfo.doILookForNewFiles()) { return false; } imageList = new ArrayList(); List files = pollingInfo.getFiles(); for (int i = 0; i < files.size(); i++) { imageList.add(new AddeImageDescriptor(files.get(i).toString())); } return true; } /** * The user changed the properties. Update me. */ protected void propertiesChanged() { PollingInfo pollingInfo = getPollingInfo(); if (pollingInfo.doILookForNewFiles()) { List newSources = pollingInfo.getFiles(); if (newSources.size() != imageList.size()) { initDataFromPollingInfo(); dataChoices = null; notifyDataChange(); } } super.propertiesChanged(); } /** * Make an ImageDataset from an array of ADDE URLs or AREA file names * * @param addeURLs array of ADDE URLs * * @return ImageDataset */ public static ImageDataset makeImageDataset(String[] addeURLs) { AddeImageDescriptor[] aids = new AddeImageDescriptor[addeURLs.length]; for (int i = 0; i < addeURLs.length; i++) { aids[i] = new AddeImageDescriptor(addeURLs[i]); } return new ImageDataset("Image data set", Arrays.asList(aids)); } /** * Make a list of image descriptors * * @param addeURLs array of ADDE URLs * * @return ImageDataset */ public static List makeImageDescriptors(String[] addeURLs) { List descriptors = new ArrayList(); for (int i = 0; i < addeURLs.length; i++) { descriptors.add(new AddeImageDescriptor(addeURLs[i])); } return descriptors; } /** * Get the location where we poll. * protected List getLocationsForPolling() { * @return true if ( !isFileBased()) { return null; } List files = new ArrayList(); for (int i = 0; i < imageList.size(); i++) { AddeImageDescriptor aid = getDescriptor(imageList.get(i)); files.add(aid.getSource()); } return files; } /** * Are we getting images from a file or from adde * * @return is the data from files */ protected boolean isFileBased() { if ((imageList == null) || (imageList.size() == 0)) { return false; } AddeImageDescriptor aid = getDescriptor(imageList.get(0)); return aid.isFromFile(); } /** * A utility method that helps us deal with legacy bundles that used to * have String file names as the id of a data choice. * * @param object May be an AddeImageDescriptor (for new bundles) or a * String that is converted to an image descriptor. * @return The image descriptor. */ public AddeImageDescriptor getDescriptor(Object object) { if (object == null) { return null; } if (object instanceof DataChoice) { object = ((DataChoice) object).getId(); } if (object instanceof ImageDataInfo) { int index = ((ImageDataInfo) object).getIndex(); if (index < myDataChoices.size()) { DataChoice dc = (DataChoice) myDataChoices.get(index); Object tmpObject = dc.getId(); if (tmpObject instanceof ImageDataInfo) { return ((ImageDataInfo) tmpObject).getAid(); } } return null; // return ((ImageDataInfo) object).getAid(); } if (object instanceof AddeImageDescriptor) { return (AddeImageDescriptor) object; } return new AddeImageDescriptor(object.toString()); } /** * This is used when we are unbundled and we may have different times than when we were saved. * Use the current set of data choices. * * @param compositeDataChoice The composite * @param dataChoices Its choices * * @return The current choices */ public List getCompositeDataChoices( CompositeDataChoice compositeDataChoice, List dataChoices) { //Force creation of data choices getDataChoices(); return !(hasBandInfo(compositeDataChoice)) ? myDataChoices : dataChoices; } /** * A hook for the derived classes to return their specific name (eg, * ADDE data source, McIDAS data source. * * @return The name of this data source. */ public abstract String getImageDataSourceName(); /** * Return the list of {@link AddeImageDescriptor}s that define this * data source. * * @return The list of image descriptors. */ public List getImageList() { return imageList; } /** * Set the list of {@link AddeImageDescriptor}s that define this data * source. * @param l The list of image descriptors. */ public void setImageList(List l) { imageList = l; } /** * Override the base class method to return the list of times we created. * * @return The list of times held by this data source. */ public List doMakeDateTimes() { imageTimes = new ArrayList(); for (Iterator iter = imageList.iterator(); iter.hasNext(); ) { Object object = iter.next(); AddeImageDescriptor aid = getDescriptor(object); if ( !aid.getIsRelative()) { DateTime imageTime = aid.getImageTime(); if (imageTime != null) { imageTimes.add(imageTime); } } else { imageTimes.add(getRelativeTimeObject(aid)); } } return imageTimes; } /** * Initialize the {@link ucar.unidata.data.DataCategory} objects that * this data source uses. */ private void makeCategories() { twoDTimeSeriesCategories = DataCategory.parseCategories("IMAGE-2D-TIME;", false); twoDCategories = DataCategory.parseCategories("IMAGE-2D;", false); bandCategories = DataCategory.parseCategories("IMAGE-BAND;", false); bandTimeSeriesCategories = DataCategory.parseCategories("IMAGE-BAND-TIME;", false); } /** * Return the list of {@link ucar.unidata.data.DataCategory} used for * single time step data. * * @return A list of categories. */ public List getTwoDCategories() { if (twoDCategories == null) { makeCategories(); } return twoDCategories; } /** * Return the list of {@link ucar.unidata.data.DataCategory} used for * multiple time step data. * * @return A list of categories. */ public List getTwoDTimeSeriesCategories() { if (twoDCategories == null) { makeCategories(); } return twoDTimeSeriesCategories; } /** * Return the list of {@link ucar.unidata.data.DataCategory} used for * single time step data with band information. * * @return A list of categories. */ public List getBandCategories() { if (bandCategories == null) { makeCategories(); } return bandCategories; } /** * Return the list of {@link ucar.unidata.data.DataCategory} used for * multiple time step data with band information. * * @return A list of categories. */ public List getBandTimeSeriesCategories() { if (bandTimeSeriesCategories == null) { makeCategories(); } return bandTimeSeriesCategories; } /** * Create the set of {@link ucar.unidata.data.DataChoice} that represent * the data held by this data source. We create one top-level * {@link ucar.unidata.data.CompositeDataChoice} that represents */ return true; * all of the image time steps. We create a set of children * {@link ucar.unidata.data.DirectDataChoice}, one for each time step. */ public void doMakeDataChoices() { String type = (String) getProperty(PROP_IMAGETYPE, TYPE_SATELLITE); List bandInfos = (List) getProperty(PROP_BANDINFO, (Object) null); Hashtable props = Misc.newHashtable(DataChoice.PROP_ICON, (type.equals(TYPE_RADAR) ? "/auxdata/ui/icons/Radar.gif" : "/auxdata/ui/icons/Satellite.gif")); List categories = (imageList.size() > 1) ? getTwoDTimeSeriesCategories() : getTwoDCategories(); // This is historical an is not added into the list of choices // for selection by the users. myCompositeDataChoice = new CompositeDataChoice(this, imageList, getName(), getDataName(), categories, props); myCompositeDataChoice.setUseDataSourceToFindTimes(true); doMakeDataChoices(myCompositeDataChoice); if ((bandInfos != null) && !bandInfos.isEmpty()) { List biCategories = (imageList.size() > 1) ? getBandTimeSeriesCategories() : getBandCategories(); /* if (bandInfos.size() == 1) { BandInfo test = (BandInfo) bandInfos.get(0); List units = test.getCalibrationUnits(); if ((units == null) || units.isEmpty() || (units.size() == 1)) { return; } } */ for (Iterator i = bandInfos.iterator(); i.hasNext(); ) { BandInfo bi = i.next(); String name = makeBandParam(bi); String catName = bi.getBandDescription(); List biSubCategories = Misc.newList(new DataCategory(catName, true)); biSubCategories.addAll(biCategories); List l = bi.getCalibrationUnits(); if (l.isEmpty() || (l.size() == 1)) { DataChoice choice = new DirectDataChoice(this, bi, name, bi.getBandDescription(), biCategories, props); addDataChoice(choice); } else { for (int j = 0; j < l.size(); j++) { Object o = l.get(j); BandInfo bi2 = new BandInfo(bi); String calUnit = o.toString(); String calibration = TwoFacedObject.getIdString(o); bi2.setPreferredUnit(calibration); name = makeBandParam(bi2); DataChoice subChoice = new DirectDataChoice(this, bi2, name, calUnit, biSubCategories, props); addDataChoice(subChoice); } } } } else { addDataChoice(myCompositeDataChoice); } } /** * Make a parmeter name for the BandInfo * * @param bi the BandInfo in question * * @return a name for the parameter */ private String makeBandParam(BandInfo bi) { StringBuffer buf = new StringBuffer(); buf.append(bi.getSensor()); buf.append("_Band"); buf.append(bi.getBandNumber()); buf.append("_"); buf.append(bi.getPreferredUnit()); return buf.toString(); } /** * Make the data choices and add them to the given composite * @param composite The parent data choice to add to */ private void doMakeDataChoices(CompositeDataChoice composite) { int cnt = 0; imageTimes = new ArrayList(); List timeChoices = new ArrayList(); myDataChoices = new ArrayList(); String type = (String) getProperty(PROP_IMAGETYPE, TYPE_SATELLITE); Hashtable props = Misc.newHashtable(DataChoice.PROP_ICON, (type.equals(TYPE_RADAR) ? "/auxdata/ui/icons/clock.gif" : "/auxdata/ui/icons/clock.gif")); for (Iterator iter = imageList.iterator(); iter.hasNext(); ) { Object object = iter.next(); AddeImageDescriptor aid = getDescriptor(object); String name = aid.toString(); DataSelection timeSelect = null; if ( !aid.getIsRelative()) { DateTime imageTime = aid.getImageTime(); if (imageTime != null) { imageTimes.add(imageTime); //timeSelect = new DataSelection (Misc.newList (imageTime)); //We will create the data choice with an index, not with the actual time. timeSelect = new DataSelection(Misc.newList(new Integer(cnt))); } } else { imageTimes.add(getRelativeTimeObject(aid)); } timeSelect = null; DataChoice choice = new DirectDataChoice(this, new ImageDataInfo(cnt, aid), composite.getName(), name, getTwoDCategories(), timeSelect, props); myDataChoices.add(choice); cnt++; timeChoices.add(choice); } //Sort the data choices. composite.replaceDataChoices(sortChoices(timeChoices)); } /** * Class ImageDataInfo Holds an index and an AddeImageDescriptor * * * @author IDV Development Team * @version $Revision: 1.76 $ */ public static class ImageDataInfo { /** The index */ private int index; /** The AID */ private AddeImageDescriptor aid; /** * Ctor for xml encoding */ public ImageDataInfo() {} /** * CTOR * * @param index The index * @param aid The aid */ public ImageDataInfo(int index, AddeImageDescriptor aid) { this.index = index; this.aid = aid; } /** * Get the index * * @return The index */ public int getIndex() { return index; } /** * Set the index * * @param v The index */ public void setIndex(int v) { index = v; } /** * Get the descriptor * * @return The descriptor */ public AddeImageDescriptor getAid() { return aid; } /** * Set the descriptor * * @param v The descriptor */ public void setAid(AddeImageDescriptor v) { aid = v; } /** * toString * * @return toString */ public String toString() { return "index:" + index + " " + aid; } } /** * Create the actual data represented by the given * {@link ucar.unidata.data.DataChoice}. * * @param dataChoice Either the * {@link ucar.unidata.data.CompositeDataChoice} * representing all time steps or a * {@link ucar.unidata.data.DirectDataChoice} * representing a single time step. * @param category Not really used. * @param dataSelection Defines any time subsets. * @param requestProperties extra request properties * * @return The image or image sequence data. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected Data getDataInner(DataChoice dataChoice, DataCategory category, DataSelection dataSelection, Hashtable requestProperties) throws VisADException, RemoteException { try { if (dataChoice instanceof CompositeDataChoice) { return makeImageSequence(myCompositeDataChoice, dataSelection); } else if (hasBandInfo(dataChoice)) { return makeImageSequence(dataChoice, dataSelection); } } finally { // System.err.println ("ImageDataSource.getDataInner:done"); } return (Data) makeImage(dataChoice, dataSelection); } /** * Override the base class method for the non composite choices. * * @param dataChoice Either the * {@link ucar.unidata.data.CompositeDataChoice} * representing all time steps or a * {@link ucar.unidata.data.DirectDataChoice} * representing a single time step. * @return The list of times represented by the given dataChoice. */ public List getAllDateTimes(DataChoice dataChoice) { if ((dataChoice instanceof CompositeDataChoice) || hasBandInfo(dataChoice)) { return super.getAllDateTimes(dataChoice); } Object dttmObject = getDateTime(dataChoice); if (dttmObject != null) { return Misc.newList(dttmObject); } return new ArrayList(); } /** * Override the base class method for the non-composite choices. * * @param dataChoice Either the * {@link ucar.unidata.data.CompositeDataChoice} * representing all time steps or a * {@link ucar.unidata.data.DirectDataChoice} * representing a single time step. * @return The list of times represented by the given dataChoice. */ public List getSelectedDateTimes(DataChoice dataChoice) { if ((dataChoice instanceof CompositeDataChoice) || hasBandInfo(dataChoice)) { return super.getSelectedDateTimes(); } Object dttmObject = getDateTime(dataChoice); if (dttmObject != null) { return Misc.newList(dttmObject); } return new ArrayList(); } /** * Utility method to get the time associated with the given dataChoice. * * @param dataChoice choice for selection * @return the associated time */ private Object getDateTime(DataChoice dataChoice) { Object id = dataChoice.getId(); AddeImageDescriptor aid = getDescriptor(id); if (aid.getIsRelative()) { return getRelativeTimeObject(aid); } else { return aid.getImageTime(); } } /** * Get the object that we use to display relative time. Relative time is defined * using an integer index, 0...n. We don't want to show the actual integer. * Rather we want to show "Third most recent", "Fourth most recent", etc. * * @param aid The image descriptor * @return The object that represents the relative time index of the aid */ private Object getRelativeTimeObject(AddeImageDescriptor aid) { return new TwoFacedObject(aid.toString(), new Integer(aid.getRelativeIndex())); } /** * Create the single image defined by the given dataChoice. * * @param dataChoice The choice. * @param subset any time subsets. * * @return The data. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected final SingleBandedImage makeImage(DataChoice dataChoice, DataSelection subset) throws VisADException, RemoteException { AddeImageDescriptor aid = getDescriptor(dataChoice.getId()); if (aid == null) { return null; } DateTime dttm = aid.getImageTime(); if ((subset != null) && (dttm != null)) { List times = getTimesFromDataSelection(subset, dataChoice); if ((times != null) && (times.indexOf(dttm) == -1)) { return null; } } return makeImage(aid, null, false, ""); } /** * Create the single image defined by the given dataChoice. * * @param aid AddeImageDescriptor * @param rangeType This is the sample rangeType. For the first image this is null and this method will immediately read the data * @param fromSequence from a sequence * @param readLabel the label * * @return The data. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private SingleBandedImage makeImage(AddeImageDescriptor aid, MathType rangeType, boolean fromSequence, String readLabel) throws VisADException, RemoteException { if (aid == null) { return null; } String source = aid.getSource(); SingleBandedImage result = (SingleBandedImage) getCache(source); if (result != null) { public boolean getCacheDataToDisk() { return result; } try { AddeImageInfo aii = aid.getImageInfo(); AreaDirectory areaDir = null; try { if (aii != null) { if (currentDirs != null) { int pos = Math.abs(aii.getDatasetPosition()); int band = 0; String bandString = aii.getBand(); if ((bandString != null) && !bandString.equals(aii.ALL)) { band = new Integer(bandString).intValue(); } //TODO: even though the band is non-zero we might only get back one band band = 0; areaDir = currentDirs[currentDirs.length - pos - 1][band]; } else { //If its absolute time then just use the AD from the descriptor if ((aii.getStartDate() != null) || (aii.getEndDate() != null)) { areaDir = aid.getDirectory(); // System.err.println("absolute time:" + areaDir.getNominalTime()); //System.err.println(" from aii:" +aii.getStartDate()); } else { // System.err.println( // "relative time without currentDirs " // + fromSequence); } } } } catch (Exception exc) { LogUtil.printMessage("error looking up area dir"); exc.printStackTrace(); return null; } if (areaDir == null) { areaDir = aid.getDirectory(); } if ( !getCacheDataToDisk()) { areaDir = null; } if ( !fromSequence || (aid.getIsRelative() && (currentDirs == null))) { areaDir = null; } if (areaDir != null) { int hash = ((aii != null) ? aii.makeAddeUrl().hashCode() : areaDir.hashCode()); //If the range type is null then we are reading the first image //and we want to read it immediately so we can get the correct range //from the data itself if (rangeType == null) { result = AreaImageFlatField.createImmediate(aid, readLabel); } else { //Else, pass in the already created range type result = AreaImageFlatField.create(aid, areaDir, rangeType, readLabel); } } else { AreaAdapter aa = new AreaAdapter(aid.getSource(), false); timeMap.put(aid.getSource(), aa.getImageStartTime()); result = aa.getImage(); aa = null; } putCache(source, result); return result; } catch (java.io.IOException ioe) { throw new VisADException("Creating AreaAdapter - " + ioe); } } /** * Reload the data */ public void reloadData() { currentDirs = null; super.reloadData(); } /** * Get whether we should cache to disk /** the ranget type */ private MathType rangeType = null; /** * Create the image sequence defined by the given dataChoice. * * @param dataChoice The choice. * @param subset any time subsets. * @return The image sequence. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected ImageSequence makeImageSequence(DataChoice dataChoice, DataSelection subset) throws VisADException, RemoteException { try { List descriptorsToUse = new ArrayList(); if (hasBandInfo(dataChoice)) { descriptorsToUse = getDescriptors(dataChoice, subset); } else { List choices = (dataChoice instanceof CompositeDataChoice) ? getChoicesFromSubset( (CompositeDataChoice) dataChoice, subset) : Arrays.asList(new DataChoice[] { dataChoice }); for (Iterator iter = choices.iterator(); iter.hasNext(); ) { DataChoice subChoice = (DataChoice) iter.next(); AddeImageDescriptor aid = getDescriptor(subChoice.getId()); if (aid == null) { continue; } DateTime dttm = aid.getImageTime(); if ((subset != null) && (dttm != null)) { List times = getTimesFromDataSelection(subset, dataChoice); if ((times != null) && (times.indexOf(dttm) == -1)) { continue; } } descriptorsToUse.add(aid); } } if (descriptorsToUse.size() == 0) { return null; } AddeImageInfo biggestPosition = null; int pos = 0; boolean anyRelative = false; //Find the descriptor with the largest position String biggestSource = null; for (Iterator iter = descriptorsToUse.iterator(); iter.hasNext(); ) { AddeImageDescriptor aid = (AddeImageDescriptor) iter.next(); if (aid.getIsRelative()) { anyRelative = true; } AddeImageInfo aii = aid.getImageInfo(); //Are we dealing with area files here? if (aii == null) { break; } // System.err.println (" aii:" +aii.makeAddeUrl()); // System.err.println (" aid:" + aid.getSource()); //Check if this is absolute time if ((aii.getStartDate() != null) || (aii.getEndDate() != null)) { biggestPosition = null; break; } if ((biggestPosition == null) || (Math.abs(aii.getDatasetPosition()) > pos)) { pos = Math.abs(aii.getDatasetPosition()); biggestPosition = aii; biggestSource = aid.getSource(); } } // System.err.println(getCacheDataToDisk() + " " + biggestPosition); //If any of them are relative then read in the list of AreaDirectorys so we can get the correct absolute times // TODO: revisit this if (getCacheDataToDisk() && anyRelative && (biggestPosition != null)) { biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDIR); /* System.err.println(biggestPosition.makeAddeUrl() + "\nfrom aid:" + biggestSource); System.err.println(biggestPosition.makeAddeUrl() + "\nfrom aii:" + biggestPosition.makeAddeUrl()); */ AreaDirectoryList adl = new AreaDirectoryList(biggestPosition.makeAddeUrl()); biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDATA); currentDirs = adl.getSortedDirs(); } else { currentDirs = null; } ThreadManager threadManager = new ThreadManager("image data reading"); //threadManager.debug = true; final ImageSequenceManager sequenceManager = new ImageSequenceManager(); int cnt = 1; DataChoice parent = dataChoice.getParent(); final List images = new ArrayList(); rangeType = null; for (Iterator iter = descriptorsToUse.iterator(); iter.hasNext(); ) { final AddeImageDescriptor aid = (AddeImageDescriptor) iter.next(); if (currentDirs != null) { int idx = Math.abs(aid.getImageInfo().getDatasetPosition()); if (idx >= currentDirs.length) { // System.err.println("skipping index:" + idx); continue; } } String label = ""; if (parent != null) { label = label + parent.toString() + " "; } else { DataCategory displayCategory = dataChoice.getDisplayCategory(); if (displayCategory != null) { label = label + displayCategory + " "; } } label = label + dataChoice.toString(); final String readLabel = "Time: " + (cnt++) + "/" + descriptorsToUse.size() + " " + label; if (rangeType == null) { try { SingleBandedImage image = makeImage(aid, rangeType, true, readLabel); if (image != null) { //This is the first one, so grab its rangeType to use for later images rangeType = ((FunctionType) image.getType()).getRange(); synchronized (images) { images.add(image); } } } catch (VisADException ve) { // this is a nested error so just print out the real thing String realError = ve.getMessage(); realError = realError.substring(realError.lastIndexOf(":") + 1); LogUtil.printMessage(realError); //return null; } } else { // have the rangeType so put reading the rest in threads threadManager.addRunnable(new ThreadManager.MyRunnable() { public void run() throws Exception { try { SingleBandedImage image = makeImage(aid, rangeType, true, readLabel); if (image != null) { synchronized (images) { images.add(image); } } } catch (VisADException ve) { // this is a nested error so just print out the real thing String realError = ve.getMessage(); realError = realError.substring( realError.lastIndexOf(":") + 1); LogUtil.printMessage(realError); //return null; } } }); } } try { threadManager.runInParallel( getDataContext().getIdv().getMaxDataThreadCount()); } catch (VisADException ve) { LogUtil.printMessage(ve.toString()); } if (images.isEmpty()) { return null; } TreeMap imageMap = new TreeMap(); for (SingleBandedImage image : images) { imageMap.put(image.getStartTime(), image); } List sortedImages = (List) new ArrayList(imageMap.values()); if ((sortedImages.size() > 0) && (sortedImages.get(0) instanceof AreaImageFlatField)) { DataRange[] sampleRanges = null; Set domainSet = null; for (SingleBandedImage sbi : sortedImages) { AreaImageFlatField aiff = (AreaImageFlatField) sbi; sampleRanges = aiff.getRanges(true); if (domainSet == null) { domainSet = aiff.getDomainSet(); } if ((sampleRanges != null) && (sampleRanges.length > 0)) { for (int rangeIdx = 0; rangeIdx < sampleRanges.length; rangeIdx++) { DataRange r = sampleRanges[rangeIdx]; if (Double.isInfinite(r.getMin()) || Double.isInfinite(r.getMax())) { sampleRanges = null; break; } } } if (sampleRanges != null) { break; } } if (sampleRanges != null) { for (SingleBandedImage sbi : sortedImages) { AreaImageFlatField aiff = (AreaImageFlatField) sbi; aiff.setSampleRanges(sampleRanges); aiff.setDomainIfNeeded(domainSet); } } } SingleBandedImage[] imageArray = (SingleBandedImage[]) sortedImages.toArray( new SingleBandedImage[sortedImages.size()]); FunctionType imageFunction = (FunctionType) imageArray[0].getType(); FunctionType ftype = new FunctionType(RealType.Time, imageFunction); return new ImageSequenceImpl(ftype, imageArray); } catch (Exception exc) { throw new ucar.unidata.util.WrapperException(exc); } } /** * Get a list of descriptors from the choice and subset * * @param dataChoice Data choice * @param subset subsetting info * * @return list of descriptors matching the selection */ private List getDescriptors(DataChoice dataChoice, DataSelection subset) { List times = getTimesFromDataSelection(subset, dataChoice); boolean usingTimeDriver = ((subset != null) && (subset.getTimeDriverTimes() != null)); if (usingTimeDriver) { times = subset.getTimeDriverTimes(); } if ((times == null) || times.isEmpty()) { times = imageTimes; } List descriptors = new ArrayList(); if (usingTimeDriver) { AddeImageDescriptor aid = getDescriptor(imageList.get(0)); if (aid.getImageInfo() != null) { try { AddeImageInfo aii = (AddeImageInfo) aid.getImageInfo().clone(); // set the start and end dates Collections.sort(times); DateTime start = (DateTime) times.get(0); DateTime end = (DateTime) times.get(times.size() - 1); // In ADDE, you can't specify something like DAY=2011256 2011257 TIME=23:45:00 01:45:00 // and expect that to be 2011256/23:45 to 2011257 01:45. Time ranges are on a per day // basis. So, we see if the starting time is a different day than the ending day and if so, // we set the start time to be 00Z on the first day an 23:59Z on the end day. // Even worse is that for archive datasets, you can't span multiple days. So make separate // requests for each day. String startDay = UtcDate.getYMD(start); String endDay = UtcDate.getYMD(end); List days = new ArrayList(); if ( !startDay.equals(endDay)) { days = getUniqueDayStrings(times); } else { days.add(startDay); } HashMap dateDir = new HashMap(); List dirTimes = new ArrayList(); for (String day : days) { startDay = day + " 00:00:00"; endDay = day + " 23:59:59"; start = DateTime.createDateTime(startDay, DateTime.DEFAULT_TIME_FORMAT); end = DateTime.createDateTime(endDay, DateTime.DEFAULT_TIME_FORMAT); aii.setStartDate(new Date((long) (start .getValue(CommonUnit .secondsSinceTheEpoch) * 1000))); aii.setEndDate(new Date((long) (end .getValue(CommonUnit .secondsSinceTheEpoch) * 1000))); // make the request for the times (AreaDirectoryList) aii.setRequestType(aii.REQ_IMAGEDIR); AreaDirectoryList ad; try { // we may be asking for a date that doesn't exist ad = new AreaDirectoryList(aii.getURLString()); } catch (AreaFileException afe) { // TODO: This is a hack because different servers return different // messages. AREA and GINI servers seem to have "no images" in the // exception message when there are no images. if (afe.getMessage().toLowerCase().indexOf( "no images") >= 0) { continue; } else { throw afe; } } AreaDirectory[][] dirs = ad.getSortedDirs(); for (int d = 0; d < dirs.length; d++) { AreaDirectory dir = dirs[d][0]; DateTime dirTime = new DateTime(dir.getNominalTime()); dateDir.put(dirTime, dir); dirTimes.add(dirTime); } } List matchedTimes = selectTimesFromList(subset, dirTimes, times); for (DateTime dirTime : matchedTimes) { AreaDirectory dir = dateDir.get(dirTime); // shouldn't happen, but what the hey if (dir == null) { continue; } AddeImageInfo newaii = (AddeImageInfo) aid.getImageInfo().clone(); newaii.setRequestType(aii.REQ_IMAGEDATA); newaii.setStartDate(dir.getNominalTime()); newaii.setEndDate(dir.getNominalTime()); setBandInfo(dataChoice, newaii); AddeImageDescriptor newaid = new AddeImageDescriptor(dir, newaii.getURLString(), newaii); newaid.setIsRelative(false); descriptors.add(newaid); } } catch (CloneNotSupportedException cnse) { System.out.println("unable to clone aii"); } catch (VisADException vader) { System.out.println("unable to get date values"); } catch (AreaFileException afe) { System.out.println("unable to make request"); } catch (Exception excp) { System.out.println("Got an exception: " + excp.getMessage()); } // we do this so save data local will work. However, if // this then gets set to be the time driver, it would not // necessarily be correct imageList = descriptors; return descriptors; } } for (Iterator iter = times.iterator(); iter.hasNext(); ) { Object time = iter.next(); AddeImageDescriptor found = null; for (Iterator iter2 = imageList.iterator(); iter2.hasNext(); ) { AddeImageDescriptor aid = getDescriptor(iter2.next()); if (aid != null) { if (aid.getIsRelative()) { Object id = (time instanceof TwoFacedObject) ? ((TwoFacedObject) time).getId() : time; if ((id instanceof Integer) && ((Integer) id).intValue() == aid.getRelativeIndex()) { found = aid; break; } } else { if (aid.getImageTime().equals(time)) { found = aid; break; } } } } if (found != null) { try { AddeImageDescriptor desc = new AddeImageDescriptor(found); //Sometimes we might have a null imageinfo if (desc.getImageInfo() != null) { AddeImageInfo aii = (AddeImageInfo) desc.getImageInfo().clone(); setBandInfo(dataChoice, aii); /* BandInfo bi = (BandInfo) dataChoice.getId(); List bandInfos = (List) getProperty(PROP_BANDINFO, (Object) null); boolean hasBand = true; //If this data source has been changed after we have create a display //then the possibility exists that the bandinfo contained by the incoming //data choice might not be valid. If it isn't then default to the first //one in the list if (bandInfos != null) { hasBand = bandInfos.contains(bi); if ( !hasBand) { // System.err.println("has band = " + bandInfos.contains(bi)); } if ( !hasBand && (bandInfos.size() > 0)) { bi = bandInfos.get(0); } else { //Not sure what to do here. } } aii.setBand("" + bi.getBandNumber()); aii.setUnit(bi.getPreferredUnit()); */ desc.setImageInfo(aii); desc.setSource(aii.getURLString()); } descriptors.add(desc); } catch (CloneNotSupportedException cnse) {} } } return descriptors; } /** * Get a list of unique YMD day strings in the list of times * * @param times list of times * * @return the list of unique strings */ private List getUniqueDayStrings(List times) { List days = new ArrayList(); for (DateTime time : times) { String dateString = UtcDate.getYMD(time); if ( !days.contains(dateString)) { days.add(dateString); } } return days; } /** * Set the band information on the data choice * * @param dataChoice the data choice * @param aii the AddeImageInfo with the image stuff */ private void setBandInfo(DataChoice dataChoice, AddeImageInfo aii) { BandInfo bi = (BandInfo) dataChoice.getId(); List bandInfos = (List) getProperty(PROP_BANDINFO, (Object) null); boolean hasBand = true; //If this data source has been changed after we have create a display //then the possibility exists that the bandinfo contained by the incoming //data choice might not be valid. If it isn't then default to the first //one in the list if (bandInfos != null) { hasBand = bandInfos.contains(bi); if ( !hasBand) { // System.err.println("has band = " + bandInfos.contains(bi)); } if ( !hasBand && (bandInfos.size() > 0)) { bi = bandInfos.get(0); } else { //Not sure what to do here. } } aii.setBand("" + bi.getBandNumber()); aii.setUnit(bi.getPreferredUnit()); } /** * Get the subset of the composite based on the selection * * @param choice composite choice * @param subset time selection * * @return subset list */ private List getChoicesFromSubset(CompositeDataChoice choice, DataSelection subset) { List choices = choice.getDataChoices(); if (subset == null) { return choices; } List times = subset.getTimes(); if (times == null) { return choices; } times = TwoFacedObject.getIdList(times); List subChoices = new ArrayList(); Object firstTime = times.get(0); if (firstTime instanceof Integer) { for (Iterator iter = times.iterator(); iter.hasNext(); ) { subChoices.add( choices.get(((Integer) iter.next()).intValue())); } } else { // TODO: what if they are DateTimes? subChoices.addAll(choices); } return subChoices; } /** * Check to see if this ImageDataSource is equal to the object * in question. * * @param o object in question * * @return true if they are the same or equivalent objects */ public boolean equals(Object o) { if ( !super.equals(o)) { return false; } if ( !getClass().equals(o.getClass())) { return false; } ImageDataSource that = (ImageDataSource) o; return ((this == that) || Misc.equals(imageList, that.imageList)); } /** * Override the hashCode method. Use name and imageList. * * @return The hashcode. */ public int hashCode() { int hashCode = getName().hashCode(); hashCode ^= imageList.hashCode(); return hashCode; } /** * Get the name for the main data object * * @return name of main data object */ public String getDataName() { return "Image Sequence"; } /** * Get an expanded description for the details display. Override * base class implementation to add more info. * * @return full description of this data source */ public String getFullDescription() { StringBuffer buf = new StringBuffer(super.getFullDescription()); buf.append("

"); List images = getImageList(); if (images != null) { for (int i = 0; i < images.size(); i++) { Object o = images.get(i); if (o instanceof AddeImageDescriptor) { AreaDirectory ad = ((AddeImageDescriptor) o).getDirectory(); if (i == 0) { buf.append( ""); } buf.append(""); buf.append(""); buf.append(""); buf.append(""); } else { if (i == 0) { buf.append( "
LocationDateSize (Lines X Elements) Band
"); String path = ((AddeImageDescriptor) o).getSource(); if (path.length() > 50) { String tmp = path; path = ""; while (tmp.length() > 50) { if (path.length() > 0) { path = path + "
"; } path = path + tmp.substring(0, 49); tmp = tmp.substring(49); } path = path + "
" + tmp; } buf.append(path); buf.append("
"); buf.append("" + ad.getNominalTime()); buf.append(""); buf.append(ad.getLines()); buf.append(" X "); buf.append(ad.getElements()); buf.append(""); buf.append("Band "); buf.append(ad.getBands()[0]); buf.append("
"); } buf.append(""); } } buf.append("
Location
"); buf.append(o.toString()); buf.append("
"); } return buf.toString(); } /** * If we are polling some directory this method gets called when * there is a new file. We set the file name, clear our state, * reload the metadata and tell listeners of the change. * * @param file new File to use. */ public void newFileFromPolling(File file) { // System.err.println("new file from polling"); initDataFromPollingInfo(); dataChoices = null; getDataChoices(); getDataContext().dataSourceChanged(this); Hashtable cache = CacheManager.findOrCreate(dataCacheKey); flushCache(); //Should be only one here CompositeDataChoice cdc = myCompositeDataChoice; //(CompositeDataChoice) getDataChoices().get(0); cdc.removeAllDataChoices(); doMakeDataChoices(cdc); for (int i = 0; i < imageList.size(); i++) { AddeImageDescriptor aid = getDescriptor(imageList.get(i)); String source = aid.getSource(); Object cachedData = cache.get(source); if (cachedData != null) { //System.err.println("keeping the cache"); putCache(source, cachedData); } } notifyDataChange(); } /** * Sort the list of data choices on their time * * @param choices The data choices * * @return The data choices sorted */ private List sortChoices(List choices) { Object[] choicesArray = choices.toArray(); Comparator comp = new Comparator() { public int compare(Object o1, Object o2) { AddeImageDescriptor aid1 = getDescriptor(o1); AddeImageDescriptor aid2 = getDescriptor(o2); if ((aid1 == null) || (aid2 == null)) { return -1; } if (aid1.getIsRelative()) { if (aid1.getRelativeIndex() < aid2.getRelativeIndex()) { return 0; } else if (aid1.getRelativeIndex() == aid2.getRelativeIndex()) { return 1; } return -1; } return aid1.getImageTime().compareTo(aid2.getImageTime()); } }; Arrays.sort(choicesArray, comp); return new ArrayList(Arrays.asList(choicesArray)); } /** * Check if the DataChoice has a BandInfo for it's Id * * @param dataChoice choice to check * * @return true if the choice ID is a BandInfo */ private boolean hasBandInfo(DataChoice dataChoice) { return dataChoice.getId() instanceof BandInfo; } /** * Called when Datasource is removed. */ public void doRemove() { super.doRemove(); myDataChoices = null; myCompositeDataChoice = null; imageTimes = null; currentDirs = null; } } >>>>>>> ce218056a5d8ea4990aed3de426631c946140deb

Solution content
/*
 * Copyright 1997-2012 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.data.imagery;


import edu.wisc.ssec.mcidas.AreaDirectory;
import edu.wisc.ssec.mcidas.AreaDirectoryList;
import edu.wisc.ssec.mcidas.AreaFileException;

import ucar.unidata.data.CompositeDataChoice;
import ucar.unidata.data.DataCategory;
import ucar.unidata.data.DataChoice;
import ucar.unidata.data.DataSelection;
import ucar.unidata.data.DataSourceDescriptor;
import ucar.unidata.data.DataSourceImpl;
import ucar.unidata.data.DirectDataChoice;
import ucar.unidata.util.CacheManager;
import ucar.unidata.util.IOUtil;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.PollingInfo;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.TwoFacedObject;

import ucar.visad.UtcDate;
import ucar.visad.data.AreaImageFlatField;



import visad.CommonUnit;
import visad.Data;
import visad.DateTime;
import visad.FunctionType;
import visad.MathType;
import visad.RealType;
import visad.Set;
import visad.VisADException;

import visad.data.DataRange;
import visad.data.mcidas.AreaAdapter;

import visad.meteorology.ImageSequence;
import visad.meteorology.ImageSequenceImpl;
import visad.meteorology.ImageSequenceManager;
import visad.meteorology.SingleBandedImage;

import visad.util.ThreadManager;


import java.io.File;

import java.rmi.RemoteException;

import java.text.SimpleDateFormat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.TreeMap;


/**
 * Abstract DataSource class for images files.
 *
 * @author IDV development team
 */
public abstract class ImageDataSource extends DataSourceImpl {

    /** Type of image, radar or satellite. Set by the chooser to disambiguate between types */
    public static final String PROP_IMAGETYPE = "prop.imagetype";

    /** radar type */
    public static final String TYPE_RADAR = "radar";

    /** satellite type */
    public static final String TYPE_SATELLITE = "satellite";

    /** satellite type */
    public static final String PROP_BANDINFO = "bandinfo";

    /** list of twod categories */
    private List twoDCategories;

    /** list of 2D time series categories */
    private List twoDTimeSeriesCategories;

    /** list of twod categories */
    private List bandCategories;

    /** list of 2D time series categories */
    private List bandTimeSeriesCategories;

    /** list of images */
    protected List imageList;

    /** list of image times */
    protected List imageTimes = new ArrayList();

    /** My composite */
    private CompositeDataChoice myCompositeDataChoice;

    /** children choices */
    private List myDataChoices = new ArrayList();


    /** current directories */
    private AreaDirectory[][] currentDirs;

    /** timeMap */
    private Hashtable timeMap = new Hashtable();


    /**
     *  The parameterless constructor for unpersisting.
     */
    public ImageDataSource() {}


    /**
     * Create a new ImageDataSource with a list of (String) images. These
     * can either be AREA files or ADDE URLs.
     *
     * @param descriptor       The descriptor for this data source.
     * @param images           Array of  file anmes or urls.
     * @param properties       The properties for this data source.
     */
    public ImageDataSource(DataSourceDescriptor descriptor, String[] images,
                           Hashtable properties) {
        super(descriptor, "Image data set", "Image data source", properties);
        if ( !initDataFromPollingInfo()) {
            setImageList(makeImageDescriptors(images));
        }
        setDescription(getImageDataSourceName());
    }



    /**
     * Create a new ImageDataSource with a list of (String) images. These
     * can either be AREA files or ADDE URLs.
     *
     * @param descriptor       The descriptor for this data source.
     * @param images           Array of  file anmes or urls.
     * @param properties       The properties for this data source.
     */
    public ImageDataSource(DataSourceDescriptor descriptor, List images,
                           Hashtable properties) {
        this(descriptor, StringUtil.listToStringArray(images), properties);
    }




    /**
     * Create a new ImageDataSource with the given {@link ImageDataset}.
     * The dataset may hold eight AREA file filepaths or ADDE URLs.
     *
     * @param descriptor    The descriptor for this data source.
     * @param ids           The dataset.
     * @param properties    The properties for this data source.
     */
    public ImageDataSource(DataSourceDescriptor descriptor, ImageDataset ids,
                           Hashtable properties) {
        super(descriptor, ids.getDatasetName(), "Image data source",
              properties);
        setImageList(new ArrayList(ids.getImageDescriptors()));
        setDescription(getImageDataSourceName());
    }


    /**
     * Reload data
     *
     * @param object  the object to reload
     * @param properties  the properties
     */
    public void reloadData(Object object, Hashtable properties) {
        if (object instanceof ImageDataset) {
            ImageDataset ids = (ImageDataset) object;
            setImageList(new ArrayList(ids.getImageDescriptors()));
        } else if (object instanceof List) {
            String[] images = StringUtil.listToStringArray((List) object);
            setImageList(makeImageDescriptors(images));
        } else {
            try {
                String[] images = (String[]) object;
                setImageList(makeImageDescriptors(images));
            } catch (Exception exc) {
                return;
            }
        }
        setDescription(getImageDataSourceName());
        reloadProperties(properties);
        reloadData();
    }


    /**
     * Handle when this data source gets new files to use at runtime (e.g., from isl)
     *
     * @param files List of files
     *
     */
    public void setNewFiles(List files) {
        setImageList(
            makeImageDescriptors(StringUtil.listToStringArray(files)));
    }




    /**
     * Update the state
     *
     * @param newObject  the new object
     * @param newProperties  the new properties
     */
    public void updateState(Object newObject, Hashtable newProperties) {
        super.updateState(newObject, newProperties);
        if (newObject instanceof ImageDataset) {
            ImageDataset ids = (ImageDataset) newObject;
            setImageList(new ArrayList(ids.getImageDescriptors()));
            setDescription(getImageDataSourceName());
        } else if (newObject instanceof List) {
            setTmpPaths((List) newObject);
        } else if (newObject instanceof String) {
            setTmpPaths(Misc.newList(newObject));
        }
    }


    /**
     * Get the paths for saving data files
     *
     * @return data paths
     */
    public List getDataPaths() {
        List             paths = new ArrayList();
        SimpleDateFormat sdf   = new SimpleDateFormat("_"
                                   + DATAPATH_DATE_FORMAT);
        try {
            for (int i = 0; i < imageList.size(); i++) {
                AddeImageDescriptor aid  = getDescriptor(imageList.get(i));
                String              path = aid.getSource();
                DateTime            dttm = (DateTime) timeMap.get(path);
                /*                if(dttm!=null) {
                    String dateString = sdf.format(ucar.visad.Util.makeDate(dttm));
                    if(path.indexOf(".area")>=0 && path.indexOf(dateString)==-1) {
                        path = path.replace(".area", dateString+".area");
                    }
                    System.err.println("path:" + path);
                    paths.add(new Object[]{path,path+dateString});
                    } else {*/
                paths.add(path);
                //                }
            }
        } catch (Exception exc) {
            throw new ucar.unidata.util.WrapperException(exc);
        }
        return paths;
    }


    /**
     * Override the init method for when this data source is unpersisted.
     * We simply check the imageList to see if this object came from a
     * legacy bundle.
     */
    public void initAfterUnpersistence() {
        super.initAfterUnpersistence();
        List tmp = getTmpPaths();
        if (tmp != null) {
            imageList = new ArrayList();
            for (int i = 0; i < tmp.size(); i++) {
                imageList.add(new AddeImageDescriptor(tmp.get(i).toString()));
            }
        }
        if ((imageList != null) && (imageList.size() > 0)
                && (imageList.get(0) instanceof String)) {
            List tmpList = imageList;
            imageList = new ArrayList();
            for (int i = 0; i < tmpList.size(); i++) {
                imageList.add(
                    new AddeImageDescriptor(tmpList.get(i).toString()));
            }
        }
        initDataFromPollingInfo();
    }



    /**
     * Can this data source cache its
     *
     * @return can cache data to disk
     */
    public boolean canCacheDataToDisk() {
        return true;
    }




    /**
     * Is this data source capable of saving its data to local disk
     *
     * @return Can save to local disk
     */
    public boolean canSaveDataToLocalDisk() {
        if (isFileBased()) {
            return false;
        }
        List bandInfos =
            (List) getProperty(PROP_BANDINFO, (Object) null);
        if ((bandInfos == null) || (bandInfos.size() == 0)) {
            return true;
        }
        if (bandInfos.size() > 1) {
            return false;
        }
        List l = bandInfos.get(0).getCalibrationUnits();
        if (l.size() > 1) {
            return false;
        }
        return true;
    }

    /**
     * Save files to local disk
     *
     * @param prefix destination dir and file prefix
     * @param loadId For JobManager
     * @param changeLinks Change internal file references
     *
     * @return Files copied
     *
     * @throws Exception On badness
     */
    protected List saveDataToLocalDisk(String prefix, Object loadId,
                                       boolean changeLinks)
            throws Exception {
        List             urls     = new ArrayList();
        List             suffixes = new ArrayList();
        SimpleDateFormat sdf      = new SimpleDateFormat("_"
                                   + DATAPATH_DATE_FORMAT);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        for (int i = 0; i < imageList.size(); i++) {
            AddeImageDescriptor aid  = getDescriptor(imageList.get(i));
            String              url  = aid.getSource();
            DateTime            dttm = (DateTime) timeMap.get(url);
            if (dttm != null) {
                suffixes.add(sdf.format(ucar.visad.Util.makeDate(dttm))
                             + ".area");
            } else {
                suffixes.add(i + ".area");
            }
            urls.add(url);
        }
        List newFiles = IOUtil.writeTo(urls, prefix, suffixes, loadId);
        // System.err.println ("files:" + newFiles);
        if (newFiles == null) {
            return null;
        }
        if (changeLinks) {
            imageList = newFiles;
        }
        return newFiles;
    }






    /**
     * Method for intializing the data.
     *
     *
     * @return result
     */
    protected boolean initDataFromPollingInfo() {
        PollingInfo pollingInfo = getPollingInfo();
        if ( !pollingInfo.getForFiles()
                || !pollingInfo.doILookForNewFiles()) {
            return false;
        }
        imageList = new ArrayList();
        List files = pollingInfo.getFiles();
        for (int i = 0; i < files.size(); i++) {
            imageList.add(new AddeImageDescriptor(files.get(i).toString()));
        }
        return true;
    }


    /**
     * The user changed the properties. Update me.
     */
    protected void propertiesChanged() {
        PollingInfo pollingInfo = getPollingInfo();
        if (pollingInfo.doILookForNewFiles()) {
            List newSources = pollingInfo.getFiles();
            if (newSources.size() != imageList.size()) {
                initDataFromPollingInfo();
                dataChoices = null;
                notifyDataChange();
            }
        }
        super.propertiesChanged();
    }



    /**
     * Make an ImageDataset from an array of ADDE URLs or AREA file names
     *
     * @param addeURLs  array of ADDE URLs
     *
     * @return ImageDataset
     */
    public static ImageDataset makeImageDataset(String[] addeURLs) {
        AddeImageDescriptor[] aids = new AddeImageDescriptor[addeURLs.length];
        for (int i = 0; i < addeURLs.length; i++) {
            aids[i] = new AddeImageDescriptor(addeURLs[i]);
        }
        return new ImageDataset("Image data set", Arrays.asList(aids));
    }


    /**
     * Make a list of image descriptors
     *
     * @param addeURLs  array of ADDE URLs
     *
     * @return ImageDataset
     */
    public static List makeImageDescriptors(String[] addeURLs) {
        List descriptors = new ArrayList();
        for (int i = 0; i < addeURLs.length; i++) {
            descriptors.add(new AddeImageDescriptor(addeURLs[i]));
        }
        return descriptors;
    }



    /**
     * Get the location where we poll.
     *
     * @return Directory to poll on.
     */
    protected List getLocationsForPolling() {
        if ( !isFileBased()) {
            return null;
        }
        List files = new ArrayList();
        for (int i = 0; i < imageList.size(); i++) {
            AddeImageDescriptor aid = getDescriptor(imageList.get(i));
            files.add(aid.getSource());
        }
        return files;
    }


    /**
     * Are we getting images from a file or from adde
     *
     * @return is the data from files
     */
    protected boolean isFileBased() {
        if ((imageList == null) || (imageList.size() == 0)) {
            return false;
        }
        AddeImageDescriptor aid = getDescriptor(imageList.get(0));
        return aid.isFromFile();
    }


    /**
     * A utility method that helps us deal with legacy bundles that used to
     * have String file names as the id of a data choice.
     *
     * @param object     May be an AddeImageDescriptor (for new bundles) or a
     *                   String that is converted to an image descriptor.
     * @return The image descriptor.
     */
    public AddeImageDescriptor getDescriptor(Object object) {
        if (object == null) {
            return null;
        }
        if (object instanceof DataChoice) {
            object = ((DataChoice) object).getId();
        }
        if (object instanceof ImageDataInfo) {
            int index = ((ImageDataInfo) object).getIndex();
            if (index < myDataChoices.size()) {
                DataChoice dc        = (DataChoice) myDataChoices.get(index);
                Object     tmpObject = dc.getId();
                if (tmpObject instanceof ImageDataInfo) {
                    return ((ImageDataInfo) tmpObject).getAid();
                }
            }
            return null;
            //            return ((ImageDataInfo) object).getAid();
        }

        if (object instanceof AddeImageDescriptor) {
            return (AddeImageDescriptor) object;
        }
        return new AddeImageDescriptor(object.toString());
    }





    /**
     * This is used when we are unbundled and we may have different times than when we were saved.
     * Use the current set of data choices.
     *
     * @param compositeDataChoice The composite
     * @param dataChoices Its choices
     *
     * @return The  current choices
     */
    public List getCompositeDataChoices(
            CompositeDataChoice compositeDataChoice, List dataChoices) {
        //Force  creation of data choices
        getDataChoices();
        return !(hasBandInfo(compositeDataChoice))
               ? myDataChoices
               : dataChoices;
    }



    /**
     * A hook for the derived classes to return their specific name (eg,
     * ADDE data source, McIDAS data source.
     *
     * @return The name of this data source.
     */
    public abstract String getImageDataSourceName();
    /**
     * Return the list of {@link AddeImageDescriptor}s that define this
     * data source.
     *
     * @return The list of image descriptors.
     */
    public List getImageList() {
        return imageList;
    }

    /**
     * Set the list of {@link AddeImageDescriptor}s that define this data
     * source.
     *
     * @param l The list of image descriptors.
     */
    public void setImageList(List l) {
        imageList = l;
    }


    /**
     * Override the base class method to return the list of times we created.
     *
     * @return The list of times held by this data source.
     */
    public List doMakeDateTimes() {
        imageTimes = new ArrayList();
        for (Iterator iter = imageList.iterator(); iter.hasNext(); ) {
            Object              object = iter.next();
            AddeImageDescriptor aid    = getDescriptor(object);
            if ( !aid.getIsRelative()) {
                DateTime imageTime = aid.getImageTime();
                if (imageTime != null) {
                    imageTimes.add(imageTime);
                }
            } else {
                imageTimes.add(getRelativeTimeObject(aid));
            }
        }
        return imageTimes;
    }


    /**
     * Initialize the {@link ucar.unidata.data.DataCategory} objects that
     * this data source uses.
     */
    private void makeCategories() {
        twoDTimeSeriesCategories =
            DataCategory.parseCategories("IMAGE-2D-TIME;", false);
        twoDCategories = DataCategory.parseCategories("IMAGE-2D;", false);
        bandCategories = DataCategory.parseCategories("IMAGE-BAND;", false);
        bandTimeSeriesCategories =
            DataCategory.parseCategories("IMAGE-BAND-TIME;", false);

    }

    /**
     * Return the list of {@link ucar.unidata.data.DataCategory} used for
     * single time step data.
     *
     * @return A list of categories.
     */
    public List getTwoDCategories() {
        if (twoDCategories == null) {
            makeCategories();
        }
        return twoDCategories;
    }

    /**
     * Return the list of {@link ucar.unidata.data.DataCategory} used for
     * multiple time step data.
     *
     * @return A list of categories.
     */

    public List getTwoDTimeSeriesCategories() {
        if (twoDCategories == null) {
            makeCategories();
        }
        return twoDTimeSeriesCategories;
    }



    /**
     * Return the list of {@link ucar.unidata.data.DataCategory} used for
     * single time step data with band information.
     *
     * @return A list of categories.
     */
    public List getBandCategories() {
        if (bandCategories == null) {
            makeCategories();
        }
        return bandCategories;
    }

    /**
     * Return the list of {@link ucar.unidata.data.DataCategory} used for
     * multiple time step data with band information.
     *
     * @return A list of categories.
     */

    public List getBandTimeSeriesCategories() {
        if (bandTimeSeriesCategories == null) {
            makeCategories();
        }
        return bandTimeSeriesCategories;
    }




    /**
     * Create the set of {@link ucar.unidata.data.DataChoice} that represent
     * the data held by this data source.  We create one top-level
     * {@link ucar.unidata.data.CompositeDataChoice} that represents
     * all of the image time steps. We create a set of children
     * {@link ucar.unidata.data.DirectDataChoice}, one for each time step.
     */
    public void doMakeDataChoices() {
        String type = (String) getProperty(PROP_IMAGETYPE, TYPE_SATELLITE);
        List bandInfos =
            (List) getProperty(PROP_BANDINFO, (Object) null);
        Hashtable props = Misc.newHashtable(DataChoice.PROP_ICON,
                                            (type.equals(TYPE_RADAR)
                                             ? "/auxdata/ui/icons/Radar.gif"
                                             : "/auxdata/ui/icons/Satellite.gif"));
        List categories = (imageList.size() > 1)
                          ? getTwoDTimeSeriesCategories()
                          : getTwoDCategories();

        // This is historical an is not added into the list of choices
        // for selection by the users.
        myCompositeDataChoice = new CompositeDataChoice(this, imageList,
                getName(), getDataName(), categories, props);
        myCompositeDataChoice.setUseDataSourceToFindTimes(true);
        doMakeDataChoices(myCompositeDataChoice);

        if ((bandInfos != null) && !bandInfos.isEmpty()) {
            List biCategories = (imageList.size() > 1)
                                ? getBandTimeSeriesCategories()
                                : getBandCategories();
            /*
            if (bandInfos.size() == 1) {
                BandInfo test  = (BandInfo) bandInfos.get(0);
                List     units = test.getCalibrationUnits();
                if ((units == null) || units.isEmpty()
                        || (units.size() == 1)) {
                    return;
                }
            }
            */
            for (Iterator i = bandInfos.iterator(); i.hasNext(); ) {
                BandInfo bi          = i.next();
                String   name        = makeBandParam(bi);
                String   catName     = bi.getBandDescription();
                List biSubCategories = Misc.newList(new DataCategory(catName,
                                           true));
                biSubCategories.addAll(biCategories);
                List l = bi.getCalibrationUnits();
                if (l.isEmpty() || (l.size() == 1)) {
                    DataChoice choice = new DirectDataChoice(this, bi, name,
                                            bi.getBandDescription(),
                                            biCategories, props);
                    addDataChoice(choice);
                } else {
                    for (int j = 0; j < l.size(); j++) {
                        Object   o           = l.get(j);
                        BandInfo bi2         = new BandInfo(bi);
                        String   calUnit     = o.toString();
                        String   calibration = TwoFacedObject.getIdString(o);
                        bi2.setPreferredUnit(calibration);
                        name = makeBandParam(bi2);
                        DataChoice subChoice = new DirectDataChoice(this,
                                                   bi2, name, calUnit,
                                                   biSubCategories, props);
                        addDataChoice(subChoice);
                    }
                }
            }
        } else {
            addDataChoice(myCompositeDataChoice);
        }
    }

    /**
     * Make a parmeter name for the BandInfo
     *
     * @param bi    the BandInfo in question
     *
     * @return  a name for the parameter
     */
    private String makeBandParam(BandInfo bi) {
        StringBuffer buf = new StringBuffer();
        buf.append(bi.getSensor());
        buf.append("_Band");
        buf.append(bi.getBandNumber());
        buf.append("_");
        buf.append(bi.getPreferredUnit());
        return buf.toString();
    }

    /**
     * Make the data choices and add them to the given composite
     *
     * @param composite The parent data choice to add to
     */
    private void doMakeDataChoices(CompositeDataChoice composite) {
        int cnt = 0;
        imageTimes = new ArrayList();
        List timeChoices = new ArrayList();
        myDataChoices = new ArrayList();
        String    type  = (String) getProperty(PROP_IMAGETYPE, TYPE_SATELLITE);
        Hashtable props = Misc.newHashtable(DataChoice.PROP_ICON,
                                            (type.equals(TYPE_RADAR)
                                             ? "/auxdata/ui/icons/clock.gif"
                                             : "/auxdata/ui/icons/clock.gif"));



        for (Iterator iter = imageList.iterator(); iter.hasNext(); ) {
            Object              object     = iter.next();
            AddeImageDescriptor aid        = getDescriptor(object);
            String              name       = aid.toString();
            DataSelection       timeSelect = null;
            if ( !aid.getIsRelative()) {
                DateTime imageTime = aid.getImageTime();
                if (imageTime != null) {
                    imageTimes.add(imageTime);
                    //timeSelect = new DataSelection (Misc.newList (imageTime));
                    //We will create the  data choice with an index, not with the actual time.
                    timeSelect =
                        new DataSelection(Misc.newList(new Integer(cnt)));
                }
            } else {
                imageTimes.add(getRelativeTimeObject(aid));
            }
            timeSelect = null;
            DataChoice choice = new DirectDataChoice(this,
                                    new ImageDataInfo(cnt, aid),
                                    composite.getName(), name,
                                    getTwoDCategories(), timeSelect, props);
            myDataChoices.add(choice);
            cnt++;
            timeChoices.add(choice);
        }
        //Sort the data choices.
        composite.replaceDataChoices(sortChoices(timeChoices));
    }



    /**
     * Class ImageDataInfo Holds an index and an AddeImageDescriptor
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.76 $
     */
    public static class ImageDataInfo {

        /** The index */
        private int index;

        /** The AID */
        private AddeImageDescriptor aid;



        /**
         * Ctor for xml encoding
         */
        public ImageDataInfo() {}

        /**
         * CTOR
         *
         * @param index The index
         * @param aid The aid
         */
        public ImageDataInfo(int index, AddeImageDescriptor aid) {
            this.index = index;
            this.aid   = aid;
        }

        /**
         * Get the index
         *
         * @return The index
         */
        public int getIndex() {
            return index;
        }

        /**
         * Set the index
         *
         * @param v The index
         */
        public void setIndex(int v) {
            index = v;
        }

        /**
         * Get the descriptor
         *
         * @return The descriptor
         */
        public AddeImageDescriptor getAid() {
            return aid;
        }

        /**
         * Set the descriptor
         *
         * @param v The descriptor
         */
        public void setAid(AddeImageDescriptor v) {
            aid = v;
        }

        /**
         * toString
         *
         * @return toString
         */
        public String toString() {
            return "index:" + index + " " + aid;
        }

    }



    /**
     * Create the actual data represented by the given
     * {@link ucar.unidata.data.DataChoice}.
     *
     * @param dataChoice        Either the
     *                          {@link ucar.unidata.data.CompositeDataChoice}
     *                          representing all time steps or a
     *                          {@link ucar.unidata.data.DirectDataChoice}
     *                          representing a single time step.
     * @param category          Not really used.
     * @param dataSelection     Defines any time subsets.
     * @param requestProperties extra request properties
     *
     * @return The image or image sequence data.
     *
     * @throws RemoteException    Java RMI problem
     * @throws VisADException     VisAD problem
     */
    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
                                DataSelection dataSelection,
                                Hashtable requestProperties)
            throws VisADException, RemoteException {

        try {
            if (dataChoice instanceof CompositeDataChoice) {
                return makeImageSequence(myCompositeDataChoice,
                                         dataSelection);
            } else if (hasBandInfo(dataChoice)) {
                return makeImageSequence(dataChoice, dataSelection);
            }

        } finally {
            //      System.err.println ("ImageDataSource.getDataInner:done");
        }

        return (Data) makeImage(dataChoice, dataSelection);
    }


    /**
     * Override the base class method for the non composite choices.
     *
     * @param dataChoice          Either the
     *                            {@link ucar.unidata.data.CompositeDataChoice}
     *                            representing all time steps or a
    }
     *                            {@link ucar.unidata.data.DirectDataChoice}
     *                            representing a single time step.
     *  @return The list of times represented by the given dataChoice.
     */
    public List getAllDateTimes(DataChoice dataChoice) {
        if ((dataChoice instanceof CompositeDataChoice)
                || hasBandInfo(dataChoice)) {
            return super.getAllDateTimes(dataChoice);
        }
        Object dttmObject = getDateTime(dataChoice);
        if (dttmObject != null) {
            return Misc.newList(dttmObject);
        }
        return new ArrayList();
    }

    /**
     * Override the base class method for the non-composite choices.
     *
     * @param dataChoice         Either the
     *                           {@link ucar.unidata.data.CompositeDataChoice}
     *                           representing all time steps or a
     *                           {@link ucar.unidata.data.DirectDataChoice}
     *                           representing a single time step.
     * @return The list of times represented by the given dataChoice.
     */
    public List getSelectedDateTimes(DataChoice dataChoice) {
        if ((dataChoice instanceof CompositeDataChoice)
                || hasBandInfo(dataChoice)) {
            return super.getSelectedDateTimes();
        }
        Object dttmObject = getDateTime(dataChoice);
        if (dttmObject != null) {
            return Misc.newList(dttmObject);
        }
        return new ArrayList();
    }


    /**
     * Utility method to get the time associated with the given dataChoice.
     *
     * @param dataChoice     choice for selection
     * @return  the associated time
     */
    private Object getDateTime(DataChoice dataChoice) {
        Object              id  = dataChoice.getId();
        AddeImageDescriptor aid = getDescriptor(id);
        if (aid.getIsRelative()) {
            return getRelativeTimeObject(aid);
        } else {
            return aid.getImageTime();
        }
    }


    /**
     * Get the object that we use to display relative time. Relative time is defined
     * using an integer index, 0...n. We don't want to show the actual integer.
     * Rather we want to show "Third most recent", "Fourth most recent", etc.
     *
     * @param aid The image descriptor
     * @return The object that represents the relative time index of the aid
     */
    private Object getRelativeTimeObject(AddeImageDescriptor aid) {
        return new TwoFacedObject(aid.toString(),
                                  new Integer(aid.getRelativeIndex()));
    }


    /**
     * Create the single image defined by the given dataChoice.
     *
     * @param dataChoice      The choice.
     * @param subset          any time subsets.
     *
     * @return The data.
     *
     * @throws RemoteException    Java RMI problem
     * @throws VisADException     VisAD problem
     */
    protected final SingleBandedImage makeImage(DataChoice dataChoice,
            DataSelection subset)
            throws VisADException, RemoteException {

        AddeImageDescriptor aid = getDescriptor(dataChoice.getId());
        if (aid == null) {
            return null;
        }
        DateTime dttm = aid.getImageTime();
        if ((subset != null) && (dttm != null)) {
            List times = getTimesFromDataSelection(subset, dataChoice);
            if ((times != null) && (times.indexOf(dttm) == -1)) {
                return null;
            }
        }
        return makeImage(aid, null, false, "");



    /**
     * Create the single image defined by the given dataChoice.
     *
     * @param aid AddeImageDescriptor
     * @param rangeType This is the sample rangeType. For the first image this is null and this method will immediately read the data
     * @param fromSequence from a sequence
     * @param readLabel  the label
     *
     * @return The data.
     *
     * @throws RemoteException    Java RMI problem
     * @throws VisADException     VisAD problem
     */
    private SingleBandedImage makeImage(AddeImageDescriptor aid,
                                        MathType rangeType,
        }
                                        boolean fromSequence,
                                        String readLabel)
            throws VisADException, RemoteException {

        if (aid == null) {
            return null;
        }
        String            source = aid.getSource();

        SingleBandedImage result = (SingleBandedImage) getCache(source);
        if (result != null) {
            return result;
        }

        try {
            AddeImageInfo aii     = aid.getImageInfo();
            AreaDirectory areaDir = null;
            try {
                if (aii != null) {
                    if (currentDirs != null) {
                        int    pos        = Math.abs(aii.getDatasetPosition());
                        int    band       = 0;
                        String bandString = aii.getBand();
                        if ((bandString != null)
                                && !bandString.equals(aii.ALL)) {
                            band = new Integer(bandString).intValue();
                        }
                        //TODO: even though the band is non-zero we might only get back one band
                        band    = 0;
                        areaDir =
                            currentDirs[currentDirs.length - pos - 1][band];
                    } else {
                        //If its absolute time then just use the AD from the descriptor
                        if ((aii.getStartDate() != null)
                                || (aii.getEndDate() != null)) {
                            areaDir = aid.getDirectory();
                            //                                System.err.println("absolute time:" + areaDir.getNominalTime());
                            //System.err.println(" from aii:" +aii.getStartDate());
                        } else {
                            //                            System.err.println(
                            //                                "relative time without currentDirs "
                            //                                + fromSequence);
                        }
                    }
                }
            } catch (Exception exc) {
                LogUtil.printMessage("error looking up area dir");
                exc.printStackTrace();
                return null;
            }

            if (areaDir == null) {
                areaDir = aid.getDirectory();
            }

            if ( !getCacheDataToDisk()) {
                areaDir = null;
            }
            if ( !fromSequence
                    || (aid.getIsRelative() && (currentDirs == null))) {
                areaDir = null;
            }

            if (areaDir != null) {
                int hash = ((aii != null)
                            ? aii.makeAddeUrl().hashCode()
                            : areaDir.hashCode());

                //If the range type is null then we are reading the first image
                //and we want to read it immediately so we can get the correct range
                //from the data itself
                if (rangeType == null) {
                    result = AreaImageFlatField.createImmediate(aid,
                            readLabel);
                } else {
                    //Else, pass in the already created range type
                    result = AreaImageFlatField.create(aid, areaDir,
                            rangeType, readLabel);
                }
            } else {
                AreaAdapter aa = new AreaAdapter(aid.getSource(), false);
                timeMap.put(aid.getSource(), aa.getImageStartTime());
                result = aa.getImage();
                aa     = null;
            }
            putCache(source, result);
            return result;
        } catch (java.io.IOException ioe) {
            throw new VisADException("Creating AreaAdapter - " + ioe);

    }

    /**
     * Reload the data
     */
    public void reloadData() {
        currentDirs = null;
        super.reloadData();
    }


    /**
     * Get whether we should cache to disk
     *
     * @return  true
     */
    public boolean getCacheDataToDisk() {
        return true;
    }

    /** the ranget type */
    private MathType rangeType = null;

    /**
     * Create the  image sequence defined by the given dataChoice.
     *
     * @param dataChoice     The choice.
     * @param subset     any time subsets.
     * @return The image sequence.
     *
     * @throws RemoteException    Java RMI problem
     * @throws VisADException     VisAD problem
     */
    protected ImageSequence makeImageSequence(DataChoice dataChoice,
            DataSelection subset)
            throws VisADException, RemoteException {

        try {
            List descriptorsToUse = new ArrayList();
            if (hasBandInfo(dataChoice)) {
                descriptorsToUse = getDescriptors(dataChoice, subset);
            } else {
                List choices = (dataChoice instanceof CompositeDataChoice)
                               ? getChoicesFromSubset(
                                   (CompositeDataChoice) dataChoice, subset)
                               : Arrays.asList(new DataChoice[] {
                                   dataChoice });
                for (Iterator iter = choices.iterator(); iter.hasNext(); ) {
                    DataChoice          subChoice = (DataChoice) iter.next();
                    AddeImageDescriptor aid       =
                        getDescriptor(subChoice.getId());
                    if (aid == null) {
                        continue;
                    }
                    DateTime dttm = aid.getImageTime();
                    if ((subset != null) && (dttm != null)) {
                        List times = getTimesFromDataSelection(subset,
                                         dataChoice);
                        if ((times != null) && (times.indexOf(dttm) == -1)) {
                            continue;
                        }
                    }
                    descriptorsToUse.add(aid);
                }
            }

            if (descriptorsToUse.size() == 0) {
                return null;
            }
            AddeImageInfo biggestPosition = null;
            int           pos             = 0;
            boolean       anyRelative     = false;
            //Find the descriptor with the largest position
            String biggestSource = null;
            for (Iterator iter =
                    descriptorsToUse.iterator(); iter.hasNext(); ) {
                AddeImageDescriptor aid = (AddeImageDescriptor) iter.next();
                if (aid.getIsRelative()) {
                    anyRelative = true;
                }
                AddeImageInfo aii = aid.getImageInfo();

                //Are we dealing with area files here?
                if (aii == null) {
                    break;
                }

                //                System.err.println (" aii:" +aii.makeAddeUrl());
                //                System.err.println (" aid:" + aid.getSource());
                //Check if this is absolute time
                if ((aii.getStartDate() != null)
                        || (aii.getEndDate() != null)) {
                    biggestPosition = null;
                    break;
                }
                if ((biggestPosition == null)
                        || (Math.abs(aii.getDatasetPosition()) > pos)) {
                    pos             = Math.abs(aii.getDatasetPosition());
                    biggestPosition = aii;
                    biggestSource   = aid.getSource();
                }
            }

            //            System.err.println(getCacheDataToDisk() + " " + biggestPosition);

            //If any of them are relative then read in the list of AreaDirectorys so we can get the correct absolute times
            // TODO:  revisit this

            if (getCacheDataToDisk() && anyRelative
                    && (biggestPosition != null)) {
                biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDIR);
                /*
                  System.err.println(biggestPosition.makeAddeUrl()
                  + "\nfrom aid:" + biggestSource);
                  System.err.println(biggestPosition.makeAddeUrl()
                  + "\nfrom aii:" + biggestPosition.makeAddeUrl());
                */
                AreaDirectoryList adl =
                    new AreaDirectoryList(biggestPosition.makeAddeUrl());
                biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDATA);
                currentDirs = adl.getSortedDirs();
            } else {
                currentDirs = null;
            }

            ThreadManager threadManager =
                new ThreadManager("image data reading");
            //threadManager.debug = true;
            final ImageSequenceManager sequenceManager =
                new ImageSequenceManager();
            int                           cnt    = 1;
            DataChoice                    parent = dataChoice.getParent();
            final List images =
                new ArrayList();
            rangeType = null;
            for (Iterator iter =
                    descriptorsToUse.iterator(); iter.hasNext(); ) {
                final AddeImageDescriptor aid =
                    (AddeImageDescriptor) iter.next();
                if (currentDirs != null) {
                    int idx =
                        Math.abs(aid.getImageInfo().getDatasetPosition());
                    if (idx >= currentDirs.length) {
                        //                        System.err.println("skipping index:" + idx);
                        continue;
                    }
                }

                String label = "";
                if (parent != null) {
                    label = label + parent.toString() + " ";
                } else {
                    DataCategory displayCategory =
                        dataChoice.getDisplayCategory();
                    if (displayCategory != null) {
                        label = label + displayCategory + " ";
                    }
                }
                label = label + dataChoice.toString();
                final String readLabel = "Time: " + (cnt++) + "/"
                                         + descriptorsToUse.size() + "  "
                                         + label;

                if (rangeType == null) {
                    try {

                        SingleBandedImage image = makeImage(aid, rangeType,
                                                      true, readLabel);
                        if (image != null) {
                            //This is the first one, so grab its rangeType to use for later images
                            rangeType =
                                ((FunctionType) image.getType()).getRange();
                            synchronized (images) {
                                images.add(image);
                            }
                        }

                    } catch (VisADException ve) {
                        // this is a nested error so just print out the real thing
                        String realError = ve.getMessage();
                        realError =
                            realError.substring(realError.lastIndexOf(":")
                                + 1);
                        LogUtil.printMessage(realError);
                        //return null;
                    }
                } else {  // have the rangeType so put reading the rest in threads
                    threadManager.addRunnable(new ThreadManager.MyRunnable() {
                        public void run() throws Exception {
                            try {

                                SingleBandedImage image = makeImage(aid,
                                                              rangeType,
                                                              true,
                                                              readLabel);
                                if (image != null) {
                                    synchronized (images) {
                                        images.add(image);
                                    }
                                }

                            } catch (VisADException ve) {
                                // this is a nested error so just print out the real thing
                                String realError = ve.getMessage();
                                realError = realError.substring(
                                    realError.lastIndexOf(":") + 1);
                                LogUtil.printMessage(realError);
                                //return null;
                            }
                        }
                    });

                }
            }

            try {
                threadManager.runInParallel(
                    getDataContext().getIdv().getMaxDataThreadCount());
            } catch (VisADException ve) {
                LogUtil.printMessage(ve.toString());
            }
            if (images.isEmpty()) {
                return null;
            }

            TreeMap imageMap = new TreeMap();
            for (SingleBandedImage image : images) {
                imageMap.put(image.getStartTime(), image);
            }
            List sortedImages =
                (List) new ArrayList(imageMap.values());
            if ((sortedImages.size() > 0)
                    && (sortedImages.get(0) instanceof AreaImageFlatField)) {
                DataRange[] sampleRanges = null;
                Set         domainSet    = null;
                for (SingleBandedImage sbi : sortedImages) {
                    AreaImageFlatField aiff = (AreaImageFlatField) sbi;
                    sampleRanges = aiff.getRanges(true);
                    if (domainSet == null) {
                        domainSet = aiff.getDomainSet();
                    }
                    if ((sampleRanges != null) && (sampleRanges.length > 0)) {
                        for (int rangeIdx = 0; rangeIdx < sampleRanges.length;
                                rangeIdx++) {
                            DataRange r = sampleRanges[rangeIdx];
                            if (Double.isInfinite(r.getMin())
                                    || Double.isInfinite(r.getMax())) {
                                sampleRanges = null;
                                break;
                            }
                        }
                    }
                    if (sampleRanges != null) {
                        break;
                    }
                }

                if (sampleRanges != null) {
                    for (SingleBandedImage sbi : sortedImages) {
                        AreaImageFlatField aiff = (AreaImageFlatField) sbi;
                        aiff.setSampleRanges(sampleRanges);
                        aiff.setDomainIfNeeded(domainSet);
                    }
                }
            }

            SingleBandedImage[] imageArray =
                (SingleBandedImage[]) sortedImages.toArray(
                    new SingleBandedImage[sortedImages.size()]);
            FunctionType imageFunction =
                (FunctionType) imageArray[0].getType();
            FunctionType ftype = new FunctionType(RealType.Time,
                                     imageFunction);
            return new ImageSequenceImpl(ftype, imageArray);
        } catch (Exception exc) {
            throw new ucar.unidata.util.WrapperException(exc);
        }

    }



    /**
     * Get a list of descriptors from the choice and subset
     *
     * @param dataChoice  Data choice
     * @param subset  subsetting info
     *
     * @return  list of descriptors matching the selection
     */
    private List getDescriptors(DataChoice dataChoice, DataSelection subset) {

        List    times = getTimesFromDataSelection(subset, dataChoice);
        boolean usingTimeDriver = ((subset != null)
                                   && (subset.getTimeDriverTimes() != null));
        if (usingTimeDriver) {
            times = subset.getTimeDriverTimes();
        }
        if ((times == null) || times.isEmpty()) {
            times = imageTimes;
        }

        List descriptors = new ArrayList();
        if (usingTimeDriver) {
            AddeImageDescriptor aid = getDescriptor(imageList.get(0));
            if (aid.getImageInfo() != null) {
                try {
                    AddeImageInfo aii =
                        (AddeImageInfo) aid.getImageInfo().clone();
                    // set the start and end dates
                    Collections.sort(times);
                    DateTime start = (DateTime) times.get(0);
                    DateTime end   = (DateTime) times.get(times.size() - 1);
                    // In ADDE, you can't specify something like DAY=2011256 2011257 TIME=23:45:00 01:45:00
                    // and expect that to be 2011256/23:45 to 2011257 01:45.  Time ranges are on a per day
                    // basis.  So, we see if the starting time is a different day than the ending day and if so,
                    // we set the start time to be 00Z on the first day an 23:59Z on the end day.
                    // Even worse is that for archive datasets, you can't span multiple days.  So make separate
                    // requests for each day.
                    String       startDay = UtcDate.getYMD(start);
                    String       endDay   = UtcDate.getYMD(end);
                    List days     = new ArrayList();
                    if ( !startDay.equals(endDay)) {
                        days = getUniqueDayStrings(times);
                    } else {
                        days.add(startDay);
                    }
                    HashMap dateDir =
                        new HashMap();
                    List dirTimes = new ArrayList();
                    for (String day : days) {
                        startDay = day + " 00:00:00";
                        endDay   = day + " 23:59:59";
                        start    = DateTime.createDateTime(startDay,
                                DateTime.DEFAULT_TIME_FORMAT);
                        end = DateTime.createDateTime(endDay,
                                DateTime.DEFAULT_TIME_FORMAT);
                        aii.setStartDate(new Date((long) (start
                            .getValue(CommonUnit
                                .secondsSinceTheEpoch) * 1000)));
                        aii.setEndDate(new Date((long) (end
                            .getValue(CommonUnit
                                .secondsSinceTheEpoch) * 1000)));
                        // make the request for the times (AreaDirectoryList)
                        aii.setRequestType(aii.REQ_IMAGEDIR);
                        AreaDirectoryList ad;
                        try {  // we may be asking for a date that doesn't exist
                            ad = new AreaDirectoryList(aii.getURLString());
                        } catch (AreaFileException afe) {
                            // TODO: This is a hack because different servers return different
                            // messages.  AREA and GINI servers seem to have "no images" in the
                            // exception message when there are no images.
                            if (afe.getMessage().toLowerCase().indexOf(
                                    "no images") >= 0) {
                                continue;
                            } else {
                                throw afe;
                            }
                        }
                        AreaDirectory[][] dirs = ad.getSortedDirs();
                        for (int d = 0; d < dirs.length; d++) {
                            AreaDirectory dir     = dirs[d][0];
                            DateTime      dirTime =
                                new DateTime(dir.getNominalTime());
                            dateDir.put(dirTime, dir);
                            dirTimes.add(dirTime);
                        }
                    }
                    List matchedTimes = selectTimesFromList(subset,
                                                      dirTimes, times);
                    for (DateTime dirTime : matchedTimes) {
                        AreaDirectory dir = dateDir.get(dirTime);
                        // shouldn't happen, but what the hey
                        if (dir == null) {
                            continue;
                        }
                        AddeImageInfo newaii =
                            (AddeImageInfo) aid.getImageInfo().clone();
                        newaii.setRequestType(aii.REQ_IMAGEDATA);
                        newaii.setStartDate(dir.getNominalTime());
                        newaii.setEndDate(dir.getNominalTime());
                        setBandInfo(dataChoice, newaii);
                        AddeImageDescriptor newaid =
                            new AddeImageDescriptor(dir,
                                newaii.getURLString(), newaii);
                        newaid.setIsRelative(false);
                        descriptors.add(newaid);
                    }
                } catch (CloneNotSupportedException cnse) {
                    System.out.println("unable to clone aii");
                } catch (VisADException vader) {
                    System.out.println("unable to get date values");
                } catch (AreaFileException afe) {
                    System.out.println("unable to make request");
                } catch (Exception excp) {
                    System.out.println("Got an exception: "
                                       + excp.getMessage());
                }
                // we do this so save data local will work.  However, if
                // this then gets set to be the time driver, it would not
                // necessarily be correct
                imageList = descriptors;
                return descriptors;
            } else if (imageList != null) {
                return imageList;
            }
        }
        for (Iterator iter = times.iterator(); iter.hasNext(); ) {
            Object              time  = iter.next();
            AddeImageDescriptor found = null;
            for (Iterator iter2 = imageList.iterator(); iter2.hasNext(); ) {
                AddeImageDescriptor aid = getDescriptor(iter2.next());
                if (aid != null) {
                    if (aid.getIsRelative()) {
                        Object id = (time instanceof TwoFacedObject)
                                    ? ((TwoFacedObject) time).getId()
                                    : time;
                        if ((id instanceof Integer)
                                && ((Integer) id).intValue()
                                   == aid.getRelativeIndex()) {
                            found = aid;
                            break;
                        }

                    } else {
                        if (aid.getImageTime().equals(time)) {
                            found = aid;
                            break;
                        }
                    }

                }
            }
            if (found != null) {
                try {
                    AddeImageDescriptor desc = new AddeImageDescriptor(found);
                    //Sometimes we might have a null imageinfo
                    if (desc.getImageInfo() != null) {
                        AddeImageInfo aii =
                            (AddeImageInfo) desc.getImageInfo().clone();
                        setBandInfo(dataChoice, aii);
                        /*
                        BandInfo bi = (BandInfo) dataChoice.getId();
                        List bandInfos =
                            (List) getProperty(PROP_BANDINFO,
                                (Object) null);
                        boolean hasBand = true;
                        //If this data source has been changed after we have create a display
                        //then the possibility exists that the bandinfo contained by the incoming
                        //data choice might not be valid. If it isn't then default to the first
                        //one in the list
                        if (bandInfos != null) {
                            hasBand = bandInfos.contains(bi);
                            if ( !hasBand) {
                                //                                System.err.println("has band = " + bandInfos.contains(bi));
                            }
                            if ( !hasBand && (bandInfos.size() > 0)) {
                                bi = bandInfos.get(0);
                            } else {
                                //Not sure what to do here.
                            }
                        }
                        aii.setBand("" + bi.getBandNumber());
                        aii.setUnit(bi.getPreferredUnit());
                        */
                        desc.setImageInfo(aii);
                        desc.setSource(aii.getURLString());
                    }
                    descriptors.add(desc);
                } catch (CloneNotSupportedException cnse) {}
            }
        }
        return descriptors;

    }

    /**
     * Get a list of unique YMD day strings in the list of times
     *
     * @param times  list of times
     *
     * @return  the list of unique strings
     */
    private List getUniqueDayStrings(List times) {
        List days = new ArrayList();
        for (DateTime time : times) {
            String dateString = UtcDate.getYMD(time);
            if ( !days.contains(dateString)) {
                days.add(dateString);
            }
        }
        return days;
    }


    /**
     * Set the band information on the data choice
     *
     * @param dataChoice  the data choice
     * @param aii  the AddeImageInfo with the image stuff
     */
    private void setBandInfo(DataChoice dataChoice, AddeImageInfo aii) {
        BandInfo       bi        = (BandInfo) dataChoice.getId();
        List bandInfos =
            (List) getProperty(PROP_BANDINFO, (Object) null);
        boolean hasBand = true;
        //If this data source has been changed after we have create a display 
        //then the possibility exists that the bandinfo contained by the incoming
        //data choice might not be valid. If it isn't then default to the first 
        //one in the list
        if (bandInfos != null) {
            hasBand = bandInfos.contains(bi);
            if ( !hasBand) {
                //                                System.err.println("has band = " + bandInfos.contains(bi));
            }
            if ( !hasBand && (bandInfos.size() > 0)) {
                bi = bandInfos.get(0);
            } else {
                //Not sure what to do here.
            }
        }
        aii.setBand("" + bi.getBandNumber());
        aii.setUnit(bi.getPreferredUnit());
    }

    /**
     * Get the subset of the composite based on the selection
     *
                            path = path + tmp.substring(0, 49);
     * @param choice  composite choice
     * @param subset  time selection
     *
     * @return subset list
     */
    private List getChoicesFromSubset(CompositeDataChoice choice,
                                      DataSelection subset) {
        List choices = choice.getDataChoices();
        if (subset == null) {
            return choices;
        }
        List times = subset.getTimes();
        if (times == null) {
            return choices;
        }
        times = TwoFacedObject.getIdList(times);
        List   subChoices = new ArrayList();
        Object firstTime  = times.get(0);
        if (firstTime instanceof Integer) {
            for (Iterator iter = times.iterator(); iter.hasNext(); ) {
                subChoices.add(
                    choices.get(((Integer) iter.next()).intValue()));
            }
        } else {  // TODO: what if they are DateTimes?
            subChoices.addAll(choices);
        }
        return subChoices;
    }


    /**
     * Check to see if this ImageDataSource is equal to the object
     * in question.
     *
     * @param o  object in question
     *
     * @return true if they are the same or equivalent objects
     */
    public boolean equals(Object o) {
        if ( !super.equals(o)) {
            return false;
        }
        if ( !getClass().equals(o.getClass())) {
            return false;
        }
        ImageDataSource that = (ImageDataSource) o;
        return ((this == that) || Misc.equals(imageList, that.imageList));
    }

    /**
     * Override the hashCode method. Use name and imageList.
     *
     * @return The hashcode.
     */
    public int hashCode() {
        int hashCode = getName().hashCode();
        hashCode ^= imageList.hashCode();
        return hashCode;
    }



    /**
     * Get the name for the main data object
     *
     * @return name of main data object
     */
    public String getDataName() {
        return "Image Sequence";
    }

    /**
     * Get an expanded description for the details display.  Override
     * base class implementation to add more info.
     *
     * @return full description of this data source
     */
    public String getFullDescription() {
        StringBuffer buf = new StringBuffer(super.getFullDescription());
        buf.append("

"); List images = getImageList(); if (images != null) { for (int i = 0; i < images.size(); i++) { Object o = images.get(i); if (o instanceof AddeImageDescriptor) { AreaDirectory ad = ((AddeImageDescriptor) o).getDirectory(); if (i == 0) { buf.append( ""); } buf.append(""); buf.append(""); buf.append(""); buf.append(""); } else { if (i == 0) { buf.append( "
LocationDateSize (Lines X Elements) Band
"); String path = ((AddeImageDescriptor) o).getSource(); if (path.length() > 50) { String tmp = path; path = ""; while (tmp.length() > 50) { if (path.length() > 0) { path = path + "
"; } tmp = tmp.substring(49); } path = path + "
" + tmp; } buf.append(path); buf.append("
"); buf.append("" + ad.getNominalTime()); buf.append(""); buf.append(ad.getLines()); buf.append(" X "); buf.append(ad.getElements()); buf.append(""); buf.append("Band "); buf.append(ad.getBands()[0]); buf.append("
"); } buf.append(""); } } buf.append("
Location
"); buf.append(o.toString()); buf.append("
"); } return buf.toString(); } /** * If we are polling some directory this method gets called when * there is a new file. We set the file name, clear our state, * reload the metadata and tell listeners of the change. * * @param file new File to use. */ public void newFileFromPolling(File file) { // System.err.println("new file from polling"); initDataFromPollingInfo(); dataChoices = null; getDataChoices(); getDataContext().dataSourceChanged(this); Hashtable cache = CacheManager.findOrCreate(dataCacheKey); flushCache(); //Should be only one here CompositeDataChoice cdc = myCompositeDataChoice; //(CompositeDataChoice) getDataChoices().get(0); cdc.removeAllDataChoices(); doMakeDataChoices(cdc); for (int i = 0; i < imageList.size(); i++) { AddeImageDescriptor aid = getDescriptor(imageList.get(i)); String source = aid.getSource(); Object cachedData = cache.get(source); if (cachedData != null) { //System.err.println("keeping the cache"); putCache(source, cachedData); } } notifyDataChange(); } /** * Sort the list of data choices on their time * * @param choices The data choices * * @return The data choices sorted */ private List sortChoices(List choices) { Object[] choicesArray = choices.toArray(); Comparator comp = new Comparator() { public int compare(Object o1, Object o2) { AddeImageDescriptor aid1 = getDescriptor(o1); AddeImageDescriptor aid2 = getDescriptor(o2); if ((aid1 == null) || (aid2 == null)) { return -1; } if (aid1.getIsRelative()) { if (aid1.getRelativeIndex() < aid2.getRelativeIndex()) { return 0; } else if (aid1.getRelativeIndex() == aid2.getRelativeIndex()) { return 1; } return -1; } return aid1.getImageTime().compareTo(aid2.getImageTime()); } }; Arrays.sort(choicesArray, comp); return new ArrayList(Arrays.asList(choicesArray)); } /** * Check if the DataChoice has a BandInfo for it's Id * * @param dataChoice choice to check * * @return true if the choice ID is a BandInfo */ private boolean hasBandInfo(DataChoice dataChoice) { return dataChoice.getId() instanceof BandInfo; } /** * Called when Datasource is removed. */ public void doRemove() { super.doRemove(); myDataChoices = null; myCompositeDataChoice = null; imageTimes = null; currentDirs = null; } }

File
ImageDataSource.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration