1 /* 2 * This file is part of ELKI: 3 * Environment for Developing KDD-Applications Supported by Index-Structures 4 * 5 * Copyright (C) 2018 6 * ELKI Development Team 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters; 22 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Iterator; 26 import java.util.List; 27 28 import de.lmu.ifi.dbs.elki.utilities.io.FormatUtil; 29 import de.lmu.ifi.dbs.elki.utilities.io.ParseUtil; 30 import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; 31 import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException; 32 import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException; 33 import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint; 34 35 /** 36 * Parameter class for a parameter specifying a list of vectors. 37 * 38 * @author Steffi Wanka 39 * @author Erich Schubert 40 * @since 0.1 41 */ 42 public class DoubleArrayListParameter extends ListParameter<DoubleArrayListParameter, List<double[]>> { 43 /** 44 * Constructs a vector list parameter with the given name and description. 45 * 46 * @param optionID Option ID 47 * @param constraint Constraint 48 * @param defaultValue Default value 49 */ DoubleArrayListParameter(OptionID optionID, ParameterConstraint<List<double[]>> constraint, List<double[]> defaultValue)50 public DoubleArrayListParameter(OptionID optionID, ParameterConstraint<List<double[]>> constraint, List<double[]> defaultValue) { 51 super(optionID, defaultValue); 52 addConstraint(constraint); 53 } 54 55 /** 56 * Constructs a vector list parameter with the given name and description. 57 * 58 * @param optionID Option ID 59 * @param constraint Constraint 60 * @param optional Optional flag 61 */ DoubleArrayListParameter(OptionID optionID, ParameterConstraint<List<double[]>> constraint, boolean optional)62 public DoubleArrayListParameter(OptionID optionID, ParameterConstraint<List<double[]>> constraint, boolean optional) { 63 super(optionID, optional); 64 addConstraint(constraint); 65 } 66 67 /** 68 * Constructs a vector list parameter with the given name and description. 69 * 70 * @param optionID Option ID 71 * @param constraint Constraint 72 */ DoubleArrayListParameter(OptionID optionID, ParameterConstraint<List<double[]>> constraint)73 public DoubleArrayListParameter(OptionID optionID, ParameterConstraint<List<double[]>> constraint) { 74 super(optionID); 75 addConstraint(constraint); 76 } 77 78 /** 79 * Constructs a vector list parameter with the given name and description. 80 * 81 * @param optionID Option ID 82 * @param defaultValue Default value 83 */ DoubleArrayListParameter(OptionID optionID, List<double[]> defaultValue)84 public DoubleArrayListParameter(OptionID optionID, List<double[]> defaultValue) { 85 super(optionID, defaultValue); 86 } 87 88 /** 89 * Constructs a vector list parameter with the given name and description. 90 * 91 * @param optionID Option ID 92 * @param optional Optional flag 93 */ DoubleArrayListParameter(OptionID optionID, boolean optional)94 public DoubleArrayListParameter(OptionID optionID, boolean optional) { 95 super(optionID, optional); 96 } 97 98 /** 99 * Constructs a vector list parameter with the given name and description. 100 * 101 * @param optionID Option ID 102 */ DoubleArrayListParameter(OptionID optionID)103 public DoubleArrayListParameter(OptionID optionID) { 104 super(optionID); 105 } 106 107 @Override getValueAsString()108 public String getValueAsString() { 109 StringBuilder buf = new StringBuilder(); 110 List<double[]> val = getValue(); 111 Iterator<double[]> valiter = val.iterator(); 112 while(valiter.hasNext()) { 113 buf.append(FormatUtil.format(valiter.next(), LIST_SEP)); 114 // Append separation character 115 if(valiter.hasNext()) { 116 buf.append(VECTOR_SEP); 117 } 118 } 119 return buf.toString(); 120 } 121 122 @Override getDefaultValueAsString()123 public String getDefaultValueAsString() { 124 StringBuilder buf = new StringBuilder(); 125 List<double[]> val = getDefaultValue(); 126 Iterator<double[]> valiter = val.iterator(); 127 while(valiter.hasNext()) { 128 buf.append(FormatUtil.format(valiter.next(), LIST_SEP)); 129 // Append separation character 130 if(valiter.hasNext()) { 131 buf.append(VECTOR_SEP); 132 } 133 } 134 return buf.toString(); 135 } 136 137 @SuppressWarnings("unchecked") 138 @Override parseValue(Object obj)139 protected List<double[]> parseValue(Object obj) throws ParameterException { 140 try { 141 List<?> l = List.class.cast(obj); 142 // do extra validation: 143 for(Object o : l) { 144 if(!(o instanceof double[])) { 145 throw new WrongParameterValueException("Wrong parameter format for parameter \"" + getOptionID().getName() + "\". Given list contains objects of different type!"); 146 } 147 } 148 // TODO: can we use reflection to get extra checks? 149 // TODO: Should we copy the list and vectors? 150 return (List<double[]>) l; 151 } 152 catch(ClassCastException e) { 153 // continue with other attempts. 154 } 155 if(obj instanceof String) { 156 String[] vectors = VECTOR_SPLIT.split((String) obj); 157 if(vectors.length == 0) { 158 throw new WrongParameterValueException("Wrong parameter format! Given list of vectors for parameter \"" + getOptionID().getName() + "\" is empty!"); 159 } 160 ArrayList<double[]> vecs = new ArrayList<>(); 161 162 double[] buf = new double[11]; 163 int used = 0; 164 for(String vector : vectors) { 165 used = 0; 166 String[] coordinates = SPLIT.split(vector); 167 for(String coordinate : coordinates) { 168 try { 169 if(used == buf.length) { 170 buf = Arrays.copyOf(buf, buf.length << 1); 171 } 172 buf[used++] = ParseUtil.parseDouble(coordinate); 173 } 174 catch(NumberFormatException e) { 175 throw new WrongParameterValueException("Wrong parameter format! Coordinates of vector \"" + vector + "\" are not valid!"); 176 } 177 } 178 vecs.add(Arrays.copyOf(buf, used)); 179 } 180 return vecs; 181 } 182 throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getOptionID().getName() + "\" requires a list of double values!"); 183 } 184 185 @Override size()186 public int size() { 187 return getValue().size(); 188 } 189 190 /** 191 * Returns a string representation of the parameter's type. 192 * 193 * @return Syntax string 194 */ 195 @Override getSyntax()196 public String getSyntax() { 197 return "<double_11,...,double_1n:...:double_m1,...,double_mn>"; 198 } 199 } 200