comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java @ 430:7ab81ff32111 2.3

merged flys-artifacts/2.3
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:10 +0200
parents 6ab62e5b05b5
children 73bc64c4a7b0
comparison
equal deleted inserted replaced
290:a6f56ed9238b 430:7ab81ff32111
1 package de.intevation.flys.artifacts;
2
3 import java.util.HashMap;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.Set;
7 import java.util.Vector;
8
9 import javax.xml.xpath.XPathConstants;
10
11 import gnu.trove.TDoubleArrayList;
12
13 import org.apache.log4j.Logger;
14
15 import org.w3c.dom.Document;
16 import org.w3c.dom.Element;
17 import org.w3c.dom.Node;
18 import org.w3c.dom.NodeList;
19
20 import de.intevation.artifacts.ArtifactFactory;
21 import de.intevation.artifacts.CallContext;
22
23 import de.intevation.artifacts.common.ArtifactNamespaceContext;
24 import de.intevation.artifacts.common.utils.XMLUtils;
25
26 import de.intevation.artifactdatabase.DefaultArtifact;
27 import de.intevation.artifactdatabase.data.DefaultStateData;
28 import de.intevation.artifactdatabase.data.StateData;
29 import de.intevation.artifactdatabase.state.State;
30 import de.intevation.artifactdatabase.state.StateEngine;
31 import de.intevation.artifactdatabase.transition.TransitionEngine;
32
33 import de.intevation.flys.model.Gauge;
34 import de.intevation.flys.model.Range;
35 import de.intevation.flys.model.River;
36
37 import de.intevation.flys.artifacts.context.FLYSContext;
38 import de.intevation.flys.artifacts.model.DischargeTables;
39 import de.intevation.flys.artifacts.model.RiverFactory;
40 import de.intevation.flys.artifacts.states.DefaultState;
41
42
43 /**
44 * The defaul FLYS artifact.
45 *
46 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
47 */
48 public abstract class FLYSArtifact extends DefaultArtifact {
49
50 /** The logger that is used in this artifact.*/
51 private static Logger logger = Logger.getLogger(FLYSArtifact.class);
52
53
54 /** The XPath that points to the input data elements of the FEED document.*/
55 public static final String XPATH_FEED_INPUT =
56 "/art:action/art:data/art:input";
57
58 /** The XPath that points to the name of the target state of ADVANCE.*/
59 public static final String XPATH_ADVANCE_TARGET =
60 "/art:action/art:target/@art:name";
61
62 /** The constant string that shows that an operation was successful.*/
63 public static final String OPERATION_SUCCESSFUL = "SUCCESS";
64
65 /** The constant string that shows that an operation failed.*/
66 public static final String OPERATION_FAILED = "FAILURE";
67
68 /** The default number of steps between the start end end of a selected Q
69 * range.*/
70 public static final int DEFAULT_Q_STEPS = 30;
71
72 /** The default step width between the start end end kilometer.*/
73 public static final double DEFAULT_KM_STEPS = 0.1;
74
75
76 /** The identifier of the current state. */
77 protected String currentStateId;
78
79 /** The identifiers of previous states on a stack.*/
80 protected Vector<String> previousStateIds;
81
82 /** The name of the artifact.*/
83 protected String name;
84
85 /** The data that have been inserted into this artifact.*/
86 protected Map<String, StateData> data;
87
88
89 /**
90 * The default constructor that creates an empty FLYSArtifact.
91 */
92 public FLYSArtifact() {
93 data = new HashMap<String, StateData>();
94 previousStateIds = new Vector<String>();
95 }
96
97
98 /**
99 * Returns the name of the concrete artifact.
100 *
101 * @return the name of the concrete artifact.
102 */
103 public abstract String getName();
104
105
106 /**
107 * Returns the FLYSContext from context object.
108 *
109 * @param context The CallContext or the FLYSContext.
110 *
111 * @return the FLYSContext.
112 */
113 protected FLYSContext getFlysContext(Object context) {
114 return context instanceof FLYSContext
115 ? (FLYSContext) context
116 : (FLYSContext) ((CallContext) context).globalContext();
117 }
118
119
120 /**
121 * Initialize the artifact and insert new data if <code>data</code> contains
122 * information necessary for this artifact.
123 *
124 * @param identifier The UUID.
125 * @param factory The factory that is used to create this artifact.
126 * @param context The CallContext.
127 * @param data Some optional data.
128 */
129 @Override
130 public void setup(
131 String identifier,
132 ArtifactFactory factory,
133 Object context,
134 Document data)
135 {
136 logger.debug("Setup this artifact with the uuid: " + identifier);
137
138 super.setup(identifier, factory, context, data);
139
140 FLYSContext flysContext = (FLYSContext) context;
141 StateEngine engine = (StateEngine) flysContext.get(
142 FLYSContext.STATE_ENGINE_KEY);
143
144 String name = getName();
145
146 logger.debug("Set initial state for artifact '" + name + "'");
147 List<State> states = engine.getStates(name);
148
149 setCurrentState(states.get(0));
150 }
151
152
153 /**
154 * Insert new data included in <code>input</code> into the current state.
155 *
156 * @param target XML document that contains new data.
157 * @param context The CallContext.
158 *
159 * @return a document that contains a SUCCESS or FAILURE message.
160 */
161 @Override
162 public Document feed(Document target, CallContext context) {
163 logger.info("FLYSArtifact.feed()");
164
165 Document doc = XMLUtils.newDocument();
166
167 XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
168 doc,
169 ArtifactNamespaceContext.NAMESPACE_URI,
170 ArtifactNamespaceContext.NAMESPACE_PREFIX);
171
172 Element result = creator.create("result");
173 doc.appendChild(result);
174
175 try {
176 saveData(target, XPATH_FEED_INPUT, context);
177 return describe(target, context);
178 }
179 catch (IllegalArgumentException iae) {
180 creator.addAttr(result, "type", OPERATION_FAILED, true);
181
182 result.setTextContent(iae.getMessage());
183 }
184
185 return doc;
186 }
187
188
189 /**
190 * This method handles request for changing the current state of an
191 * artifact. It is possible to step forward or backward.
192 *
193 * @param target The incoming ADVANCE document.
194 * @param context The CallContext.
195 *
196 * @return a document that contains a SUCCESS or FAILURE message.
197 */
198 public Document advance(Document target, CallContext context) {
199 Document doc = XMLUtils.newDocument();
200
201 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
202 doc,
203 ArtifactNamespaceContext.NAMESPACE_URI,
204 ArtifactNamespaceContext.NAMESPACE_PREFIX);
205
206 Element result = ec.create("result");
207
208 String targetState = XMLUtils.xpathString(
209 target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE);
210
211 logger.info("FLYSArtifact.advance() to '" + targetState + "'");
212
213 if (isStateReachable(targetState, context)) {
214 logger.info("Advance: Step forward");
215
216 Vector<String> prev = getPreviousStateIds();
217 prev.add(getCurrentStateId());
218
219 setCurrentStateId(targetState);
220
221 return describe(target, context);
222 }
223 else if (isPreviousState(targetState, context)) {
224 logger.info("Advance: Step back to");
225
226 Vector<String> prevs = getPreviousStateIds();
227 int targetIdx = prevs.indexOf(targetState);
228 int start = prevs.size() - 1;
229
230 for (int i = start; i >= targetIdx; i--) {
231 String prev = prevs.get(i);
232 logger.debug("Remove state id '" + prev + "'");
233 prevs.remove(prev);
234 }
235
236 setCurrentStateId(targetState);
237
238 return describe(target, context);
239 }
240
241 logger.warn("Advance: Cannot advance to '" + targetState + "'");
242 ec.addAttr(result, "type", OPERATION_FAILED, true);
243
244 doc.appendChild(result);
245
246 return doc;
247 }
248
249
250 /**
251 * Returns the identifier of the current state.
252 *
253 * @return the identifier of the current state.
254 */
255 protected String getCurrentStateId() {
256 return currentStateId;
257 }
258
259
260 /**
261 * Sets the identifier of the current state.
262 *
263 * @param id the identifier of a state.
264 */
265 protected void setCurrentStateId(String id) {
266 currentStateId = id;
267 }
268
269
270 /**
271 * Set the current state of this artifact. <b>NOTE</b>We don't store the
272 * State object itself - which is not necessary - but its identifier. So
273 * this method will just call the setCurrentStateId() method with the
274 * identifier of <i>state</i>.
275 *
276 * @param state The new current state.
277 */
278 protected void setCurrentState(State state) {
279 setCurrentStateId(state.getID());
280 }
281
282
283 /**
284 * Returns the current state of the artifact.
285 *
286 * @return the current State of the artifact.
287 */
288 protected State getCurrentState(Object context) {
289 FLYSContext flysContext = getFlysContext(context);
290 StateEngine engine = (StateEngine) flysContext.get(
291 FLYSContext.STATE_ENGINE_KEY);
292
293 return engine.getState(getCurrentStateId());
294 }
295
296
297 /**
298 * Returns the vector of previous state identifiers.
299 *
300 * @return the vector of previous state identifiers.
301 */
302 protected Vector<String> getPreviousStateIds() {
303 return previousStateIds;
304 }
305
306
307 /**
308 * Adds a new StateData item to the data pool of this artifact.
309 *
310 * @param name the name of the data object.
311 * @param data the data object itself.
312 */
313 protected void addData(String name, StateData data) {
314 this.data.put(name, data);
315 }
316
317
318 /**
319 * This method returns a specific StateData object that is stored in the
320 * data pool of this artifact.
321 *
322 * @param name The name of the data object.
323 *
324 * @return the StateData object if existing, otherwise null.
325 */
326 public StateData getData(String name) {
327 return data.get(name);
328 }
329
330
331 /**
332 * This method stores the data that is contained in the FEED document.
333 *
334 * @param feed The FEED document.
335 * @param xpath The XPath that points to the data nodes.
336 */
337 public void saveData(Document feed, String xpath, CallContext context)
338 throws IllegalArgumentException
339 {
340 if (feed == null || xpath == null || xpath.length() == 0) {
341 throw new IllegalArgumentException("error_feed_no_data");
342 }
343
344 NodeList nodes = (NodeList) XMLUtils.xpath(
345 feed,
346 xpath,
347 XPathConstants.NODESET,
348 ArtifactNamespaceContext.INSTANCE);
349
350 if (nodes == null || nodes.getLength() == 0) {
351 throw new IllegalArgumentException("error_feed_no_data");
352 }
353
354 int count = nodes.getLength();
355 logger.debug("Try to save " + count + " data items.");
356
357 for (int i = 0; i < count; i++) {
358 Node node = nodes.item(i);
359
360 String name = XMLUtils.xpathString(
361 node, "@art:name", ArtifactNamespaceContext.INSTANCE);
362 String value = XMLUtils.xpathString(
363 node, "@art:value", ArtifactNamespaceContext.INSTANCE);
364
365 if (name != null && value != null) {
366 logger.debug("Save data item for '" + name + "' : " + value);
367
368 addData(name, new DefaultStateData(name, null, null, value));
369 }
370 }
371
372 State current = getCurrentState(context);
373 DefaultState toValidate = (DefaultState) fillState(current);
374
375 toValidate.validate(this, context);
376 }
377
378
379 /**
380 * This method fills a state object with the data that have been inserted to
381 * this artifact. This is necessary to use the isStateReachable() method,
382 * because the Transitions need to know about the inserted data.
383 *
384 * @param state The state that needs to be filled with data.
385 *
386 * @return the filled state.
387 */
388 protected State fillState(State state) {
389 Map<String, StateData> stateData = state.getData();
390
391 if (stateData == null) {
392 return state;
393 }
394
395 Set<String> keys = stateData.keySet();
396
397 for (String key: keys) {
398 StateData tmp = getData(key);
399
400 if (tmp != null) {
401 StateData data = stateData.get(key);
402 data.setValue(tmp.getValue());
403 }
404 }
405
406 return state;
407 }
408
409
410 /**
411 * Determines if the state with the identifier <i>stateId</i> is reachable
412 * from the current state. The determination itself takes place in the
413 * TransitionEngine.
414 *
415 * @param stateId The identifier of a state.
416 * @param context The context object.
417 *
418 * @return true, if the state specified by <i>stateId</i> is reacahble,
419 * otherwise false.
420 */
421 protected boolean isStateReachable(String stateId, Object context) {
422 logger.debug("Determine if the state '" + stateId + "' is reachable.");
423
424 FLYSContext flysContext = getFlysContext(context);
425
426 State currentState = fillState(getCurrentState(context));
427 StateEngine sEngine = (StateEngine) flysContext.get(
428 FLYSContext.STATE_ENGINE_KEY);
429
430 TransitionEngine tEngine = (TransitionEngine) flysContext.get(
431 FLYSContext.TRANSITION_ENGINE_KEY);
432
433 return tEngine.isStateReachable(this, stateId, currentState, sEngine);
434 }
435
436
437 /**
438 * Determines if the state with the identifier <i>stateId</i> is a previous
439 * state of the current state.
440 *
441 * @param stateId The target state identifier.
442 * @param context The context object.
443 */
444 protected boolean isPreviousState(String stateId, Object context) {
445 logger.debug("Determine if the state '" + stateId + "' is old.");
446
447 Vector<String> prevs = getPreviousStateIds();
448 if (prevs.contains(stateId)) {
449 return true;
450 }
451
452 return false;
453 }
454
455
456 /**
457 * Returns the selected River object based on the 'river' data that might
458 * have been inserted by the user.
459 *
460 * @return the selected River or null if no river has been chosen yet.
461 */
462 public River getRiver() {
463 StateData dRiver = getData("river");
464
465 return dRiver != null
466 ? RiverFactory.getRiver((String) dRiver.getValue())
467 : null;
468 }
469
470
471 /**
472 * Returns the selected distance of points.
473 *
474 * @return the selected distance or points.
475 */
476 public double[] getDistance() {
477 StateData dFrom = getData("ld_from");
478 StateData dTo = getData("ld_to");
479 StateData dLocations = getData("ld_locations");
480
481 if (dFrom != null && dTo != null) {
482 return getDistanceByRange(dFrom, dTo);
483 }
484 else if (dLocations != null) {
485 double[] locations = getLocations();
486 return new double[] { locations[0], locations[locations.length-1] };
487 }
488
489 logger.warn("No data found for distance determination!");
490
491 return null;
492 }
493
494
495 /**
496 * This method returns the given distance
497 *
498 * @return an array with lower and upper kilometer range for each
499 * intersected gauge.
500 */
501 public double[][] getSplittedDistance() {
502 double[] dist = getDistance();
503 List<Gauge> gauges = getGauges();
504
505 int num = gauges != null ? gauges.size() : 0;
506
507 double[][] res = new double[num][2];
508
509 for (int i = 0; i < num; i++) {
510 Range range = gauges.get(i).getRange();
511
512 double lower = range.getA().doubleValue();
513 double upper = range.getB().doubleValue();
514
515 res[i][0] = dist[0] < lower ? lower : dist[0];
516 res[i][1] = dist[1] > upper ? upper : dist[1];
517 }
518
519 return res;
520 }
521
522
523 /**
524 * Returns the selected locations based on a given array of locations.
525 *
526 * @param locations The StateData that contains the locations.
527 *
528 * @return the selected locations.
529 */
530 public double[] getLocations() {
531 StateData dLocations = getData("ld_locations");
532 String locationStr = dLocations != null
533 ? (String) dLocations.getValue()
534 : "";
535
536 if (locationStr == null || locationStr.length() == 0) {
537 logger.warn("No valid location string found!");
538 return null;
539 }
540
541 String[] tmp = locationStr.split(" ");
542 TDoubleArrayList locations = new TDoubleArrayList();
543
544 for (String l: tmp) {
545 try {
546 locations.add(Double.parseDouble(l));
547 }
548 catch (NumberFormatException nfe) {
549 logger.warn(nfe, nfe);
550 }
551 }
552
553 locations.sort();
554
555 return locations.toNativeArray();
556 }
557
558
559 /**
560 * Returns the selected distance based on a given range (from, to).
561 *
562 * @param dFrom The StateData that contains the lower value.
563 * @param dTo The StateData that contains the upper value.
564 *
565 * @return the selected distance.
566 */
567 protected double[] getDistanceByRange(StateData dFrom, StateData dTo) {
568 double from = Double.parseDouble((String) dFrom.getValue());
569 double to = Double.parseDouble((String) dTo.getValue());
570
571 return new double[] { from, to };
572 }
573
574
575 /**
576 * Returns the selected Kms.
577 *
578 * @param distance An 2dim array with [lower, upper] values.
579 *
580 * @return the selected Kms.
581 */
582 public double[] getKms(double[] distance) {
583 StateData dStep = getData("ld_step");
584
585 if (dStep == null) {
586 logger.warn("No step width given. Cannot compute Kms.");
587 return null;
588 }
589
590 double step = Double.parseDouble((String) dStep.getValue());
591
592 // transform step from 'm' into 'km'
593 step = step / 1000;
594
595 if (step == 0d) {
596 step = DEFAULT_KM_STEPS;
597 }
598
599 return getExplodedValues(distance[0], distance[1], step);
600 }
601
602
603 /**
604 * Returns the selected Kms.
605 *
606 * @return the selected kms.
607 */
608 public double[] getKms() {
609 double[] distance = getDistance();
610 return getKms(distance);
611 }
612
613
614 /**
615 * Returns the gauge based on the current distance and river.
616 *
617 * @return the gauge.
618 */
619 public Gauge getGauge() {
620 River river = getRiver();
621 double[] dist = getDistance();
622
623 if (logger.isDebugEnabled()) {
624 logger.debug("Determine gauge for:");
625 logger.debug("... river: " + river.getName());
626 logger.debug("... distance: " + dist[0] + " - " + dist[1]);
627 }
628
629 Gauge gauge = river.determineGauge(dist[0], dist[1]);
630
631 String name = gauge != null ? gauge.getName() : "'n/a";
632 logger.debug("Found gauge: " + name);
633
634 return gauge;
635 }
636
637
638 /**
639 * Returns the gauges that match the selected kilometer range.
640 *
641 * @return the gauges based on the selected kilometer range.
642 */
643 public List<Gauge> getGauges() {
644 River river = getRiver();
645 double[] dist = getDistance();
646
647 return river.determineGauges(dist[0], dist[1]);
648 }
649
650
651 /**
652 * This method returns the Q values.
653 *
654 * @return the selected Q values or null, if no Q values are selected.
655 */
656 public double[] getQs() {
657 StateData dMode = getData("wq_mode");
658 StateData dSingle = getData("wq_single");
659
660 String mode = dMode != null ? (String) dMode.getValue() : "";
661
662 if (mode.equals("Q")) {
663 if (dSingle != null) {
664 return getSingleWQValues();
665 }
666 else {
667 return getWQTriple();
668 }
669 }
670 else {
671 logger.warn("You try to get Qs, but W has been inserted.");
672 return null;
673 }
674 }
675
676
677 /**
678 * Returns the Q values based on a specified kilometer range.
679 *
680 * @param range A 2dim array with lower and upper kilometer range.
681 *
682 * @return an array of Q values.
683 */
684 public double[] getQs(double[] range) {
685 StateData dMode = getData("wq_mode");
686 StateData dValues = getData("wq_values");
687
688 String mode = dMode != null ? (String) dMode.getValue() : "";
689
690 if (mode.equals("Q")) {
691 return getWQForDist(range);
692 }
693
694 logger.warn("You try to get Qs, but Ws has been inserted.");
695 return null;
696 }
697
698
699 /**
700 * Returns the W values based on a specified kilometer range.
701 *
702 * @param range A 2dim array with lower and upper kilometer range.
703 *
704 * @return an array of W values.
705 */
706 public double[] getWs(double[] range) {
707 StateData dMode = getData("wq_mode");
708 StateData dValues = getData("wq_values");
709
710 String mode = dMode != null ? (String) dMode.getValue() : "";
711
712 if (mode.equals("W")) {
713 return getWQForDist(range);
714 }
715
716 logger.warn("You try to get Ws, but Qs has been inserted.");
717 return null;
718 }
719
720
721 /**
722 * This method returns the W values.
723 *
724 * @return the selected W values or null, if no W values are selected.
725 */
726 public double[] getWs() {
727 StateData dMode = getData("wq_mode");
728 StateData dSingle = getData("wq_single");
729
730 String mode = dMode != null ? (String) dMode.getValue() : "";
731
732 if (mode.equals("W")) {
733 if (dSingle != null) {
734 return getSingleWQValues();
735 }
736 else {
737 return getWQTriple();
738 }
739 }
740 else {
741 logger.warn("You try to get Qs, but W has been inserted.");
742 return null;
743 }
744 }
745
746
747 /**
748 * Returns the Qs for a number of Ws. This method makes use of
749 * DischargeTables.getQForW().
750 *
751 * @param ws An array of W values.
752 *
753 * @return an array of Q values.
754 */
755 public double[] getQsForWs(double[] ws) {
756 logger.debug("FLYSArtifact.getQsForWs");
757
758 River r = getRiver();
759 Gauge g = getGauge();
760
761 DischargeTables dt = new DischargeTables(r.getName(), g.getName());
762 Map<String, double [][]> tmp = dt.getValues();
763
764 double[][] values = tmp.get(g.getName());
765 double[] qs = new double[ws.length];
766
767 for (int i = 0; i < ws.length; i++) {
768 qs[i] = dt.getQForW(values, ws[i]);
769 }
770
771 return qs;
772 }
773
774
775 /**
776 * This method returns the given W or Q values for a specific range
777 * (inserted in the WQ input panel for discharge longitudinal sections).
778 *
779 * @param dist A 2dim array with lower und upper kilometer values.
780 *
781 * @return an array of W or Q values.
782 */
783 protected double[] getWQForDist(double[] dist) {
784 logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]);
785 StateData data = getData("wq_values");
786
787 if (data == null) {
788 logger.warn("Missing wq values!");
789 return null;
790 }
791
792 String dataString = (String) data.getValue();
793 String[] ranges = dataString.split(":");
794
795 for (String range: ranges) {
796 String[] parts = range.split(";");
797
798 double lower = Double.parseDouble(parts[0]);
799 double upper = Double.parseDouble(parts[1]);
800
801 if (lower <= dist[0] && upper >= dist[1]) {
802 String[] values = parts[2].split(",");
803
804 int num = values.length;
805 double[] res = new double[num];
806
807 for (int i = 0; i < num; i++) {
808 try {
809 res[i] = Double.parseDouble(values[i]);
810 }
811 catch (NumberFormatException nfe) {
812 logger.warn(nfe, nfe);
813 }
814 }
815
816 return res;
817 }
818 }
819
820 logger.warn("Specified range for WQ not found!");
821
822 return null;
823 }
824
825
826 /**
827 * This method returns an array of inserted WQ triples that consist of from,
828 * to and the step width.
829 *
830 * @return an array of from, to and step width.
831 */
832 protected double[] getWQTriple() {
833 StateData dFrom = getData("wq_from");
834 StateData dTo = getData("wq_to");
835
836 if (dFrom == null || dTo == null) {
837 logger.warn("Missing start or end value for range.");
838 return null;
839 }
840
841 double from = Double.parseDouble((String) dFrom.getValue());
842 double to = Double.parseDouble((String) dTo.getValue());
843
844 StateData dStep = getData("wq_step");
845
846 if (dStep == null) {
847 logger.warn("No step width given. Cannot compute Qs.");
848 return null;
849 }
850
851 double step = Double.parseDouble((String) dStep.getValue());
852
853 // if no width is given, the DEFAULT_Q_STEPS is used to compute the step
854 // width. Maybe, we should round the value to a number of digits.
855 if (step == 0d) {
856 double diff = to - from;
857 step = diff / DEFAULT_Q_STEPS;
858 }
859
860 return getExplodedValues(from, to, step);
861 }
862
863
864 /**
865 * Returns an array of inserted WQ double values stored as whitespace
866 * separated list.
867 *
868 * @return an array of W or Q values.
869 */
870 protected double[] getSingleWQValues() {
871 StateData dSingle = getData("wq_single");
872
873 if (dSingle == null) {
874 logger.warn("Cannot determine single WQ values. No data given.");
875 return null;
876 }
877
878 String tmp = (String) dSingle.getValue();
879 String[] strValues = tmp.split(" ");
880
881 TDoubleArrayList values = new TDoubleArrayList();
882
883 for (String strValue: strValues) {
884 try {
885 values.add(Double.parseDouble(strValue));
886 }
887 catch (NumberFormatException nfe) {
888 logger.warn(nfe, nfe);
889 }
890 }
891
892 values.sort();
893
894 return values.toNativeArray();
895 }
896
897
898 /**
899 * Returns an array of double values. The values contained in this array
900 * begin with the value <i>from</i> and end with the value <i>to</i>. The
901 * number of values in the result array depends on the <i>step</i> width.
902 *
903 * @param from The lower value.
904 * @param to The upper value.
905 * @param step The step width between two values in the result array.
906 *
907 * @return an array of double values.
908 */
909 public double[] getExplodedValues(double from, double to, double step) {
910 double lower = from;
911
912 double diff = to - from;
913 double tmp = diff / step;
914 int num = (int) Math.ceil(tmp) ;
915
916 double[] values = new double[num];
917
918 for (int idx = 0; idx < num; idx++) {
919 values[idx] = lower;
920 lower += step;
921 }
922
923 return values;
924 }
925 }
926 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org