/** * Copyright 2014 SAP AG * * 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. */ package org.aim.api.measurement.dataset; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.aim.api.measurement.AbstractRecord; import org.lpe.common.util.LpeSupportedTypes; /** * A Wrapped record wraps a set of records which have the same input parameter * configuration values. * * @author Alexander Wert */ public class DatasetRow implements IDatasetElement, Iterable<String[]> { private final TreeSet<Parameter> parameters; private int recordStructureHash; private final List<AbstractRecord> records; private final Class<? extends AbstractRecord> recordType; /** * Constructor. For internal use only * * @param recordType * type of the underlying records of the created wrappedRecord * @param parameters * additional input parameters for this wrapped record * @param records * records to add */ protected DatasetRow(final Class<? extends AbstractRecord> recordType, final TreeSet<Parameter> parameters, final List<AbstractRecord> records) { this.recordType = recordType; this.parameters = parameters; if (records != null && !records.isEmpty()) { this.records = records; AbstractRecord record = records.get(0); List<String> parNames = record.getNonMetricParameterNames(); List<Object> parValues = record.getNonMetricValues(); for (int i = 0; i < parNames.size(); i++) { addParameter(new Parameter(parNames.get(i), parValues.get(i))); } } else { this.records = new ArrayList<AbstractRecord>(); } setRecordStructureHash(getInputParameterNames().hashCode() + recordType.hashCode()); } @Override public Set<ParameterSelection> getAllParameterConfigurations() { Set<ParameterSelection> result = new HashSet<ParameterSelection>(); result.add(new ParameterSelection().select(getParameters())); return result; } @Override public Set<String> getInputParameterNames() { Set<String> result = new HashSet<String>(); for (Parameter p : getParameters()) { result.add(p.getName()); } return result; } @Override public Class<? extends AbstractRecord> getRecordType() { return recordType; } @Override public List<Object> getValues(String name) { List<Object> result = new ArrayList<Object>(); for (Parameter par : getParameters()) { if (par.getName().equals(name)) { result.add(par.getValue()); } } if (result.isEmpty()) { for (AbstractRecord rec : getRecords()) { result.add(rec.getValue(name)); } } return result; } @SuppressWarnings("unchecked") @Override public <S> List<S> getValues(String name, Class<S> type) { List<S> result = new ArrayList<S>(); boolean first = true; for (Parameter par : getParameters()) { if (par.getName().equals(name)) { result.add(par.getValue(type)); } } if (result.isEmpty()) { for (AbstractRecord rec : getRecords()) { if (first) { String typeStr = rec.getType(name).getSimpleName(); if (!LpeSupportedTypes.get(typeStr).equals(LpeSupportedTypes.get(type.getSimpleName()))) { throw new IllegalArgumentException("Invalid type for observation property " + name); } first = false; } result.add((S) rec.getValue(name)); } } return result; } @Override public Set<String> getObservationPropertiesNames() { try { if (getRecords().isEmpty()) { return recordType.newInstance().getMetricParameterNames(); } else { return getRecords().get(0).getMetricParameterNames(); } } catch (Exception e) { throw new RuntimeException(e); } } @Override public List<AbstractRecord> getRecords() { return records; } @SuppressWarnings("unchecked") @Override public <T extends AbstractRecord> List<T> getRecords(Class<T> type) { if (!type.equals(getRecordType())) { return new ArrayList<T>(); } List<T> resultList = new ArrayList<>(); for (AbstractRecord record : getRecords()) { resultList.add((T) record); } return resultList; } @Override public int size() { return records.size(); } @Override public String[] getHeader() { if (records.isEmpty()) { return null; } return getHeader(records.get(0), getParameters()); } @Override public String[] getTypes() { if (records.isEmpty()) { return null; } return getTypes(records.get(0), getParameters()); } @Override public Set<Object> getValueSet(String name) { return new HashSet<Object>(getValues(name)); } @Override public <S> Set<S> getValueSet(String name, Class<S> type) { return new HashSet<S>(getValues(name, type)); } @Override public StringArrayIterator iterator() { return new StringArrayIterator(); } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((recordType == null) ? 0 : recordType.hashCode()); result = prime * result + ((records == null) ? 0 : records.hashCode()); return result; } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } DatasetRow other = (DatasetRow) obj; if (recordType == null) { if (other.recordType != null) { return false; } } else if (!recordType.equals(other.recordType)) { return false; } if (records == null) { if (other.records != null) { return false; } } else if (!records.equals(other.records)) { return false; } return true; } /** * @param parameter * the parameter to add to the wrapper */ private void addParameter(final Parameter parameter) { getParameters().add(parameter); } /** * @return the recordStructureHash */ protected int getRecordStructureHash() { return recordStructureHash; } /** * @param recordStructureHash * the recordStructureHash to set */ protected void setRecordStructureHash(int recordStructureHash) { this.recordStructureHash = recordStructureHash; } /** * @return the input parameters */ public Set<Parameter> getParameters() { return parameters; } /** * Iterates the wrapped record returning string array representations. * * @author Alexander Wert * */ public class StringArrayIterator implements Iterator<String[]> { int index = 0; String[] extParValues = null; @Override public boolean hasNext() { return index + 1 <= records.size(); } @Override public String[] next() { if (!hasNext()) { return null; } AbstractRecord abstRec = records.get(index); String[] abstRecArray = abstRec.toStringArray(); if (extParValues == null) { extParValues = getExternalParValues(abstRec, parameters); } index++; return concatArrays(abstRecArray, extParValues); } @Override public void remove() { // not supported } } /** * Creates the String array header for the given record and parameters. * * @param record * record * @param parameters * set of parameters * @return string array representing the header */ public static String[] getHeader(AbstractRecord record, Set<Parameter> parameters) { List<String> recParNames = record.getNonMetricParameterNames(); List<String> extParNamesList = new ArrayList<String>(); for (Parameter par : parameters) { if (!recParNames.contains(par.getName())) { extParNamesList.add(par.getName()); } } return concatArrays(record.getAllParameterNames().toArray(new String[0]), extParNamesList.toArray(new String[0])); } /** * Creates the String array types header for the given record and * parameters. * * @param record * record * @param parameters * set of parameters * @return string array representing the type header */ public static String[] getTypes(AbstractRecord record, Set<Parameter> parameters) { List<String> recParTypes = new ArrayList<String>(); for (String parName : record.getAllParameterNames()) { recParTypes.add(LpeSupportedTypes.get(record.getType(parName).getSimpleName()).toString()); } List<String> recParNames = record.getNonMetricParameterNames(); List<String> extParTypeList = new ArrayList<String>(); for (Parameter par : parameters) { if (!recParNames.contains(par.getName())) { extParTypeList.add(par.getValue().getClass().getSimpleName()); } } return concatArrays(recParTypes.toArray(new String[0]), extParTypeList.toArray(new String[0])); } /** * * @param record * record * @param parameters * parameter set * @return string array representation of the values */ public static String[] getValueArray(AbstractRecord record, Set<Parameter> parameters) { String[] abstRecArray = record.toStringArray(); String[] extParValues = getExternalParValues(record, parameters); return concatArrays(abstRecArray, extParValues); } private static String[] concatArrays(String[] array1, String[] array2) { int aLen = array1.length; int bLen = array2.length; String[] result = new String[aLen + bLen]; System.arraycopy(array1, 0, result, 0, aLen); System.arraycopy(array2, 0, result, aLen, bLen); return result; } private static String[] getExternalParValues(AbstractRecord abstRec, Set<Parameter> parameters) { List<String> recParNames = abstRec.getNonMetricParameterNames(); List<String> extParValuesList = new ArrayList<String>(); for (Parameter par : parameters) { if (!recParNames.contains(par.getName())) { extParValuesList.add(par.getValue().toString()); } } return extParValuesList.toArray(new String[0]); } }