gt: 				double[] times = exchangeItem.getTimes();
context: /* MOD_V2.0
  * Copyright (c) 2012 OpenDA Association
  * All rights reserved.
  *
  * This file is part of OpenDA.
  *
  * OpenDA 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 3 of
  * the License, or (at your option) any later version.
  *
  * OpenDA 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 OpenDA.  If not, see <http://www.gnu.org/licenses/>.
  */
  package org.openda.observers;
  
  import org.openda.blackbox.config.BBUtils;
  import org.openda.blackbox.interfaces.IoObjectInterface;
  import org.openda.exchange.NonMissingStochObserverGridTimeSeriesExchangeItem;
  import org.openda.exchange.TimeInfo;
  import org.openda.interfaces.*;
  import org.openda.uncertainties.UncertaintyEngine;
  import org.openda.uncertainties.pdfs.NormalDistribution;
  import org.openda.uncertainties.pdfs.PDF;
  import org.openda.utils.*;
  import org.openda.utils.Vector;
  import org.openda.utils.geometry.GeometryUtils;
  
  import java.io.File;
  import java.io.Serializable;
  import java.util.*;
  
  /**
   * Stochastic Observer based on IOobjects
   */
  public class IoObjectStochObserver extends Instance implements IStochObserver {
  
  	IoObjectStochObserverConfig ioObjectStochObserverConfig = null;
  	private UncertaintyEngine uncertaintyEngine = null;
  
  	private List<IoObjectInterface> ioObjects = new ArrayList<IoObjectInterface>();
  	private List<IDataObject> dataObjects = new ArrayList<IDataObject>();
  	//TODO change to LinkedHashMap and remove exchangeItemIds list. AK
  	//exchangeItems in map can be wrapped within a NonMissingStochObserverExchangeItem or a NonMissingStochObserverGridTimeSeriesExchangeItem.
  	private HashMap<String, IPrevExchangeItem> exchangeItems = null;
  	private List<String> exchangeItemIds = null;
  
  	private HashMap<IPrevExchangeItem, SelectedTimeIndexRange> selectedTimeIndices = null;
  	private boolean timesEqualForAllItems = false;
  
  	//total number of selected observation values, taking into account all exchangeItems, all selected times and all grid cells.
  	private int totalSelectedObservationValueCount = 0;
  	//number of selected observation values for each exchangeItem.
  	private HashMap<String, Integer> selectedObservationValueCounts = null;
  
  	private IoObjectStochObsDescriptions observationDescriptions;
  	//only used to cache values, in case method getValues is called multiple times.
  	private TreeVector valuesAsTreeVector = null;
  	private static final int maxStorageSizeForValuesAsTreeVector = 4096;
  	private boolean atLeastOneStdDevIsFactor = false;
      private double beginTimeAsMJD = Double.MIN_VALUE;
      private double endTimeAsMJD = Double.MAX_VALUE;
  
      // IConfigurable methods
  
  	public void initialize(File workingDir, String[] arguments) {
  
  		// Read the configuration
  		File configFile;
  		configFile = new File(workingDir, arguments[0]);
  		if (!configFile.exists()) {
  			throw new RuntimeException("IoObjectStochObserver config file not found: " + configFile.getAbsolutePath());
  		}
  		IoObjectStochObserverConfigReader configReader = new IoObjectStochObserverConfigReader(configFile);
  		ioObjectStochObserverConfig = configReader.getIoObjectStochObserverConfig();
  
  		// Create the uncertainty module.
  		// For the time being, we always expect the openda UncertaintyEngine.
  		// This may change if we introduce IUncertainty.
  		Class<UncertaintyEngine> expectedClassType = UncertaintyEngine.class;
  		IoObjectStochObserverConfig.IoStochObsUncertaintyConfig uncertaintyModuleConfig = ioObjectStochObserverConfig.getUncertaintyModuleConfig();
  		String className = (uncertaintyModuleConfig.getClassName() != null) ?
  				uncertaintyModuleConfig.getClassName() : UncertaintyEngine.class.getName();
  		uncertaintyEngine = (UncertaintyEngine) ObjectSupport.createNewInstance(className, expectedClassType);
  		uncertaintyEngine.initialize(uncertaintyModuleConfig.getWorkingDir(), uncertaintyModuleConfig.getArguments());
  		uncertaintyEngine.startCheckingUncertainItems();
  
  		// Create the ioObjects, and gather the (output) exchange items.
  		this.ioObjects.clear();
  		this.dataObjects.clear();
  		exchangeItems = new HashMap<String, IPrevExchangeItem>();
  		exchangeItemIds = new ArrayList<String>();
  		boolean timeTypeKnown = false;
  		boolean isTimeDependent = false;
  		//for each ioObject store the uncertain exchangeItems in this.exchangeItems map and check if these are all of the same type (either all time dependent or all time independent).
  		//Important note: this code assumes that all uncertain exchangeItemIds are unique, even if exchangeItems from multiple ioObjects are used.
  		for (IoObjectStochObserverConfig.IoStochObsIoObjectConfig ioObjectConfig : ioObjectStochObserverConfig.getIoObjectConfigs()) {
  			IoObjectInterface ioObject = BBUtils.createIoObjectInstance(ioObjectConfig.getWorkingDir(), ioObjectConfig.getClassName(),
  					ioObjectConfig.getFileName(), ioObjectConfig.getArguments());
  
              IPrevExchangeItem[] ioExchangeItems;
              if (ioObject != null) {
              	this.ioObjects.add(ioObject);
  			    ioExchangeItems = ioObject.getExchangeItems();
              } else {
                  IDataObject iDataObject = BBUtils.createDataObject(ioObjectConfig.getWorkingDir(), ioObjectConfig.getClassName(),
  					ioObjectConfig.getFileName(), ioObjectConfig.getArguments());
                  this.dataObjects.add(iDataObject);
                  String[] iDOexchangeItemIDs = iDataObject.getExchangeItemIDs();
                  ioExchangeItems = new IPrevExchangeItem[iDOexchangeItemIDs.length];
                  for (int i=0; i<iDOexchangeItemIDs.length; i++){
  					ioExchangeItems[i] = iDataObject.getDataObjectExchangeItem(iDOexchangeItemIDs[i]);
                  }
              }
  
  			//default missingValue is Double.NaN.
              double missingValue = ioObjectConfig.getMissingValue();
  			for (IPrevExchangeItem ioExchangeItem : ioExchangeItems) {
  				//only items that are configured to be uncertain are used here.
  				if (uncertaintyEngine.checkIfItemIsUncertain(ioExchangeItem.getId())) {
  					boolean checkTimeDependence;
  					if (ioExchangeItem instanceof IGridTimeSeriesExchangeItem) {//if grid.
  						checkTimeDependence = true;
  					} else {//if not a grid.
  						//can only determine whether depends on time if ioExchangeItem has values,
  						//in other words for exchangeItems with no values at all, do not perform the check on time-dependence.
  						double[] values = ioExchangeItem.getValuesAsDoubles();
  						checkTimeDependence = values != null && values.length > 0;
  					}
  
  					if (checkTimeDependence) {
  					if (ioExchangeItem.getTimes() != null && ioExchangeItem.getTimes().length > 0) {
  						//if current item is time dependent.
  						if (timeTypeKnown && !isTimeDependent) throw createInconsistentTimeSettingException(configFile);
  						isTimeDependent = true;
  					} else {
  						//if current item is not time dependent.
  						if (timeTypeKnown && isTimeDependent) throw createInconsistentTimeSettingException(configFile);
  						isTimeDependent = false;
  					}
  					timeTypeKnown = true;
  					}
  
                      // get rid off missing values.
                      // WAS: if (ioExchangeItem instanceof SwanResults.SwanResult){
  					boolean removeMissingValues = ioObjectStochObserverConfig.removeMissingValues();
                      	// NonMissingStochObserverExchangeItem does not work if times are missing
                      	if(ioExchangeItem.getTimes()==null){removeMissingValues=false;}
  
  					if (removeMissingValues) {
  						IExchangeItem nonMissingExchangeItem;
  						if (ioExchangeItem instanceof IGridTimeSeriesExchangeItem) {
  							nonMissingExchangeItem = new NonMissingStochObserverGridTimeSeriesExchangeItem((IGridTimeSeriesExchangeItem) ioExchangeItem);
  						} else {
  							nonMissingExchangeItem = new NonMissingStochObserverExchangeItem(ioExchangeItem, missingValue);
  						}
  						exchangeItemIds.add(nonMissingExchangeItem.getId());
  						exchangeItems.put(nonMissingExchangeItem.getId(), nonMissingExchangeItem);
  					} else {
  						exchangeItemIds.add(ioExchangeItem.getId());
  						exchangeItems.put(ioExchangeItem.getId(), ioExchangeItem);
  					}
  				}
  			}
  		}
  		if (exchangeItems.isEmpty()) {
  				throw new RuntimeException(
  					"The stoch observer is empty. Did you specify the right uncertain items? Stoch obs config file: " +
  							configFile.getAbsolutePath());
  		}
  
  		if (isTimeDependent) {
  			//for each uncertain exchangeItem store the full timeIndex range in selectedTimeIndices map.
  			selectedTimeIndices = new HashMap<IPrevExchangeItem, SelectedTimeIndexRange>();
  			SelectedTimeIndexRange lastSelectedIndices = null;
  			timesEqualForAllItems = true;
  			double[] timeSelected = null;
  			double[] timeLastSelected = null;
  			for (String exchangeItemId : exchangeItemIds) {
  				IPrevExchangeItem exchangeItem = exchangeItems.get(exchangeItemId);
  				timeLastSelected = timeSelected;
  				timeSelected = exchangeItem.getTimes();
  				boolean sameTimesAsPreviousExchangeItem = true;
  				if (lastSelectedIndices != null) {
  					//lastSelectedIndices.getStart() is always 0 here.
  					sameTimesAsPreviousExchangeItem = exchangeItem.getTimes().length == lastSelectedIndices.getEnd();
  					if (sameTimesAsPreviousExchangeItem){
  						for (int iTime=0; iTime<timeSelected.length; iTime++){
  							if (timeSelected[iTime]!=timeLastSelected[iTime]){
  								sameTimesAsPreviousExchangeItem = false;
  								break;
  							}
  						}
  					}
  				}
  				timesEqualForAllItems &= sameTimesAsPreviousExchangeItem;
  				SelectedTimeIndexRange selectedIndices = (sameTimesAsPreviousExchangeItem && lastSelectedIndices != null) ? lastSelectedIndices : new SelectedTimeIndexRange(0, exchangeItem.getTimes().length);
  				selectedTimeIndices.put(exchangeItem, selectedIndices);
  				lastSelectedIndices = selectedIndices;
  			}
  		}
  
  		initializeCounts();
  		initializeAtLeastOneStdDevIsFactor();
  		uncertaintyEngine.endCheckingUncertainItems();
  		observationDescriptions = new IoObjectStochObsDescriptions(exchangeItemIds, exchangeItems, null, this);
  	}
  
  	private RuntimeException createInconsistentTimeSettingException(File configFile) {
  		return new RuntimeException(
  				"The IoObject(s) contain(s) a mixture of " +
  						" time dependent and time independent exchange items. IoObjectStochObserver config file: " +
  						configFile.getAbsolutePath());
  	}
  
  	/**
  	 * The only purpose of this method is to initialize this.totalSelectedObservationValueCount and this.selectedObservationValueCounts.
  	 */
  	private void initializeCounts() {
  		//assume 1 value per time stamp per exchange item per grid cell.
  		//If item is not time dependent, then that is the same as a time series with only one time stamp.
  		//If item is not a grid, then that is the same as a grid with only one cell.
  
  		totalSelectedObservationValueCount = 0;
  		selectedObservationValueCounts = new HashMap<String, Integer>();
  		for (String exchangeItemId : exchangeItemIds) {
  			IPrevExchangeItem exchangeItem = exchangeItems.get(exchangeItemId);
  
  			int exchangeItemSelectedValueCount = 0;
  			if (selectedTimeIndices != null && selectedTimeIndices.get(exchangeItem) != null) {//if time dependent.
  				SelectedTimeIndexRange selectedIndices = selectedTimeIndices.get(exchangeItem);
  				for (int timeIndex = selectedIndices.getStart(); timeIndex < selectedIndices.getEnd(); timeIndex++) {
  					int gridCellCount = GeometryUtils.getGridCellCount(exchangeItem, timeIndex);
  					exchangeItemSelectedValueCount += gridCellCount;
  				}
  			} else {//if not time dependent.
  				int gridCellCount = GeometryUtils.getGridCellCount(exchangeItem, -1);
  				exchangeItemSelectedValueCount += gridCellCount;
  			}
  
  			selectedObservationValueCounts.put(exchangeItem.getId(), exchangeItemSelectedValueCount);
  			totalSelectedObservationValueCount += exchangeItemSelectedValueCount;
  		}
  	}
  
  	// IStochObserver methods
  
  	public IStochObserver createSelection(String selection) {
  		throw new UnsupportedOperationException("org.openda.observers.IoObjectStochObserver.createSelection(String selection): Not implemented yet.");
  	}
  
  	/**
  	 * @param selectedTime can be a single time or a timeSpan. StartTime and endTime are both inclusive.
  	 * @return copy of this stochObserver with only data for the selected time.
  	 */
  	public IStochObserver createSelection(ITime selectedTime) {
  
  		IoObjectStochObserver child = this.createEmptyChild();
  
  		if (selectedTimeIndices != null) {
  			// Time dependent observation values. For each exchange item, select the time indices
  
  			// Time stamp or interval
  			boolean isSpan = selectedTime.isSpan();
  			if (isSpan) {
  				child.beginTimeAsMJD = selectedTime.getBeginTime().getMJD();
  				child.endTimeAsMJD = selectedTime.getEndTime().getMJD();
  			} else {
  				child.beginTimeAsMJD = child.endTimeAsMJD = selectedTime.getMJD();
  			}
  
  			child.selectedTimeIndices = new HashMap<IPrevExchangeItem, SelectedTimeIndexRange>();
  
  			//for each exchangeItem select the times within the given timeSpan and put these in child.
  			SelectedTimeIndexRange lastSelectedIndices = null;
  			child.timesEqualForAllItems = true;
  			for (String exchangeItemId : exchangeItemIds) {
  				IPrevExchangeItem exchangeItem = exchangeItems.get(exchangeItemId);
  
  