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