TfsResultImpl.java

// @formatter:off
/*******************************************************************************
 * This file is part of JMad. Copyright (c) 2008-2011, CERN. All rights reserved. Licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy
 * of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in
 * writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
 * OF ANY KIND, either express or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
// @formatter:on

package cern.accsoft.steering.jmad.domain.result.tfs;

import static cern.accsoft.steering.jmad.domain.result.tfs.TfsDoubles.parseTfsDouble;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import cern.accsoft.steering.jmad.JMadConstants;
import cern.accsoft.steering.jmad.domain.result.ResultType;
import cern.accsoft.steering.jmad.domain.var.MadxVariable;
import cern.accsoft.steering.jmad.domain.var.enums.MadxTwissVariable;
import cern.accsoft.steering.jmad.util.MadxVarType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TfsResultImpl implements TfsResult {

    /** the class logger */
    private static final Logger LOGGER = LoggerFactory.getLogger(TfsResultImpl.class);

    /** The representation of the summary table for this result */
    private TfsSummary tfsSummary;

    /**
     * If the result includes the elementnames, then we store the index of the element-names for fast access.
     */
    private final Map<String, Integer> elementIndizes = new HashMap<String, Integer>();

    /** here the Lists of values are stored. */
    private final Map<String, List<String>> valueLists = new LinkedHashMap<String, List<String>>();

    /** also the double values are stored to improve performance */
    private final Map<String, List<Double>> doubleValueLists = new LinkedHashMap<String, List<Double>>();

    /**
     * In order to be able to work with simple array-list we additionly store the keys seperately:
     */
    private final List<String> keys = new ArrayList<String>();

    /** The types of the variables (columns in the file) */
    private final Map<String, MadxVarType> varTypes = new HashMap<String, MadxVarType>();

    //
    // public methods needed for filling the result
    //

    /**
     * clears all internal variables.
     */
    public void clear() {
        elementIndizes.clear();
        varTypes.clear();
        valueLists.clear();
        doubleValueLists.clear();
        keys.clear();
    }

    /**
     * adds an empty column to the internal data.
     * 
     * @param key the key for this column.
     */
    public void createColumn(String key) {
        keys.add(unifyKey(key));
        valueLists.put(unifyKey(key), new ArrayList<String>());
    }

    /**
     * sets the Type of the column corresponding to the given key
     * 
     * @param key the key for which to set the type
     * @param varType the type for the key
     */
    public void setVarType(String key, MadxVarType varType) {
        varTypes.put(unifyKey(key), varType);
    }

    /**
     * adds a new line of data to the dataset.
     * 
     * @param values the values to set as Strings.
     */
    public void addRow(List<String> values) {
        int columnNumber = 0;
        Collection<List<String>> lists = valueLists.values();
        for (List<String> list : lists) {
            list.add(values.get(columnNumber));

            // if this is the column with element-name, then store the index
            if (unifyKey(MadxTwissVariable.NAME.getMadxName()).equalsIgnoreCase(keys.get(columnNumber))) {
                elementIndizes.put(values.get(columnNumber).toLowerCase(), list.size() - 1);
            }
            columnNumber++;
        }
    }

    /**
     * converts the data to Double and returns as ArrayList.
     * 
     * @param key the key for which to get the data
     * @return the ArrayList with values for one key (column in file)
     * @throws TfsResultException if the calculation of the data fails
     */
    private List<Double> calcDoubleData(String key) throws TfsResultException {
        ArrayList<Double> doubleList = new ArrayList<Double>();
        List<String> valueList = valueLists.get(key);

        if (!valueLists.containsKey(key)) {
            LOGGER.error("No Data for key '" + key + "'");
            return null;
        }

        if (!getVarType(key).equals(MadxVarType.DOUBLE)) {
            LOGGER.error("Data for key '" + key + "' is not of type Double!");
            return null;
        }

        for (String value : valueList) {
            try {
                doubleList.add(parseTfsDouble(value));
            } catch (NumberFormatException e) {
                throw new TfsResultException("Error while converting value '" + value + "' to Double", e);
            }
        }

        return doubleList;
    }

    /**
     * Validates, if all values can be correctly retrieved. If not, it throws an exception.
     * 
     * @throws TfsResultException if the verification fails.
     */
    public void verify() throws TfsResultException {
        for (String key : keys) {
            MadxVarType varType = getVarType(key);
            if (varType == null) {
                throw new TfsResultException("Could not determine vartype vor key " + key.toString()
                        + ". Dont know how to verify!");
            }
            if (varType == MadxVarType.STRING) {
                if (getStringData(key) == null) {
                    throw new TfsResultException("Some String-data is null!");
                }
            } else if (varType == MadxVarType.DOUBLE) {
                if (getDoubleData(key) == null) {
                    throw new TfsResultException("Some Double-data is null!");
                }
            } else {
                throw new TfsResultException("Result contains Data of type " + varType.toString()
                        + ". Dont know how to verify!");
            }
        }
    }

    /**
     * Converts all double data for fast retrieval.
     * 
     * @throws TfsResultException if the conversion from string to double values is not possible
     */
    public void convert() throws TfsResultException {
        doubleValueLists.clear();
        for (String key : keys) {
            MadxVarType varType = getVarType(key);
            if (MadxVarType.DOUBLE.equals(varType)) {
                try {
                    List<Double> doubleValues = calcDoubleData(key);
                    doubleValueLists.put(key, doubleValues);
                } catch (TfsResultException e) {
                    throw new TfsResultException("Double-data cannot be calculated!", e);
                }
            }
        }
    }

    /**
     * ensures that all keys are the same case.
     * 
     * @param key the key to unify
     * @return the converted key.
     */
    private static String unifyKey(String key) {
        return key.toUpperCase(JMadConstants.DEFAULT_LOCALE);
    }

    //
    // methods of interface TfsResult
    //

    @Override
    public int getColumnCount() {
        return valueLists.size();
    }

    @Override
    public Integer getElementIndex(String elementName) {
        String key = elementName.toLowerCase();
        return elementIndizes.get(key);
    }

    @Override
    public List<Double> getDoubleData(MadxVariable resultVariable) {
        return getDoubleData(resultVariable.getMadxName());
    }

    @Override
    public List<Double> getDoubleData(String key) {
        return this.createImmutableList(doubleValueLists.get(unifyKey(key)));
    }

    @Override
    public List<String> getStringData(MadxVariable resultVariable) {
        return getStringData(resultVariable.getMadxName());
    }

    @Override
    public List<String> getStringData(String key) {
        return this.createImmutableList(valueLists.get(unifyKey(key)));
    }

    /**
     * make the input list unmodifiable. checks if the input list is <code>null</code> and returns <code>null</code> in
     * this case.
     * 
     * @param <T> the generic type of the list elements
     * @param input the input list
     * @return the immutable list or <code>null</code> if input was <code>null</code>
     */
    private <T> List<T> createImmutableList(List<T> input) {
        if (input == null) {
            return null;
        } else {
            return Collections.unmodifiableList(input);
        }
    }

    @Override
    public List<String> getKeys() {
        return keys;
    }

    @Override
    public MadxVarType getVarType(String key) {
        return varTypes.get(unifyKey(key));
    }

    @Override
    public MadxVarType getVarType(MadxVariable var) {
        return getVarType(var.getMadxName());
    }

    //
    // Methods of interface Result
    //

    @Override
    public final ResultType getResultType() {
        return ResultType.TFS_RESULT;
    }

    @Override
    public TfsSummary getSummary() {
        return this.tfsSummary;
    }

    public void setTfsSummary(TfsSummary tfsSummary) {
        this.tfsSummary = tfsSummary;
    }
}