Mercurial > dive4elements > river
changeset 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 | a6f56ed9238b (current diff) b8377caf4515 (diff) |
children | 29a15392e229 |
files | |
diffstat | 86 files changed, 14399 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/ChangeLog Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,1734 @@ +2011-05-17 Ingo Weinzierl <ingo@intevation.de> + + Tagged RELEASE 0.1 + +2011-05-16 Hans Plum <hans@intevation.de> + + * NEWS: + New. Giving some user specific perspective to new functionality and + changes. This file references releases dates only; details can be find + in the client module at flys-client. + +2011-05-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + First step to calculate "W fuer ungleichwertige Abfluesse" correctly. + flys/issue55 + + * src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java: + New. Remaps "gleichwertige" Q values to the corresponding + "ungleichwertige" Q values depending on km. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Remap the Q values "ungleichwertig" depending on the + "gleichwertige" ones. + +2011-05-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + First step to fix flys/issue69 + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + New code path to implement the calculation of "Wasserstand/Wasspiegellage" + correctly. TODO 1: Use new path in UI. TODO 2: Remove unused old code. + +2011-05-13 Ingo Weinzierl <ingo@intevation.de> + + * Changes: Prepared Changes for the upcoming release 2.3 - see Changes + file to get to know about the changes of the version numbers. + +2011-05-13 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-37 + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: This + state would be happy if there is a UI provider called "river_panel". + +2011-05-11 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added new + methods that return the requested chart size as integer array [width, + height]. The requested size is read from the incomding request document. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: The size + of a chart is no longer static. The requested size is fetched using + ChartGenerator.getSize(). + +2011-05-11 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-52 + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + The X-Axis of such a chart is inverted, if the head of the river is not + at kilometer 0. This type of charts always have the head of the river at + the left side. + +2011-05-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Make incoming XML symmetric to DistanceInfoService. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-47 + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Adjusted temporarily the color of the W, Q and corrected W curves to + distinguish each other. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Adjusted the plot of xy charts - the gridlines are displayed now. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-53 + + * src/main/java/de/intevation/flys/artifacts/resources/Resources.java: + Added a method that returns the preferred locale based on the available + locales of the server and the desired locales of the request (CallMeta). + + * src/main/java/de/intevation/flys/exports/AbstractExporter.java: Added a + method that creates a number formatter with minimum and maximum digits. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java, + src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + Formatted the number values of the CSV exports. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RangeWithValues.java: + New. A data structure that enables us to save a data triple: a range + that consist of lower and upper double value and a set of values that + belong to this range. + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: + Implemented the validation of W/Q values. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/AbstractExporter.java: New + method to retrieve i18n messages based on keys. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java, + src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + Added headers for CSV exports. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added new i18n strings for + CSV headers. + +2011-05-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Fetches river name from incoming XML document. If no river is given all + infos about all rivers are listed. + + * src/main/resources/metadata/template.xml: Templates honors the 'river' + parameter. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Extended to pass parameters to the templating. Added support for + type conversion. + + * src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java: + Take parameters as an initial stack frame. + + * src/main/java/de/intevation/flys/artifacts/services/meta/TypeConverter.java: + New. Converts types off stacked variables. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Added a + method to add subtitles to charts. The implementation in this class does + not add any subtitle. Concrete subclasses may override this method to + add some. + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Add subtitles to charts. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n templates for + compound messages (chart subtitles). + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/resources/Resources.java: + Added new methods to retrieve translated compound messages. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/OutGenerator.java: Added a + method to set the master artifact that should be used for some special + operations. + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java, + src/main/java/de/intevation/flys/exports/AbstractExporter.java: + Implement the setMasterArtifact() method of the interface. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Call OutGenerator.setMasterArtifact(). + + NOTE: The determination of the master artifact needs to be implemented! + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Removed + hard coded dev code that defined a WQ mode. + +2011-05-09 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Stripped ugly extra whitespace from output introduced by + templating. + +2011-05-09 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/resources/metadata/template.xml: Added forgotten + columns of fixation WSTs. + +2011-05-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added a + method to retrieve i18n strings. + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Enabled i18n support for chart title and axes labels. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n strings for the + chart types above. + +2011-05-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java: + New. This OutGenerator exports the data of a discharge longitudinal + section computation. + + * doc/conf/conf.xml: Added the DischargeLongitudinalSectionExporter. + + * doc/conf/artifacts/winfo.xml: Added the exporter with CSV facet to the + discharge_longitudinal_section state. + +2011-05-07 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + Added code to make back jump correction work with both + potential flow directions. + +2011-05-06 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/resources/metadata/template.xml: + Added data cage configuration for 'Längsschnitt'. + +2011-05-06 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added the missing Q facet for discharge + longitudinal sections. + + * src/main/java/de/intevation/flys/artifacts/model/WQCKms.java: New. A + derived dataset to store W/Q values with corrected Ws for a kilometer + range. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Some new + methods and a new constructor to initialize this data object with a + predefined set of values. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New + methods to retrieve the W/Q values for the 'discharge longitudinal + section' computation. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: New + methods to retrieve and compute data used for the 'discharge + longitudinal section' computation. + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: + Bugfix: just write the ranges of gauges into the DESCRIBE if the + 'wq_values' data item is required. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n messages used in + the DESCRIBE of the WQAdapted state. + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Changed the title of the y-axis (now 'W [NN+m]'). + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Improved the transition model to reach the + output state for creating 'discharge longitudinal section' charts. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a + method that returns all gauges of the selected river based on a the + given kilometer range. + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: New. + This state creates a set of elements for the DESCRIBE that consist of a + tuple of kilometer values. The number of elements depend on the number + of gauges intersected by the given kilometer range. + + * src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java: + New. This state is the output state that is reached after the 'discharge + longitudinal section' computation has been chosen. + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Default + step width between two kilometers added - if no step width is given, + this default width is used. + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Fixed a potential NullPointerException if there is just a single + kilometer given to create a longitudinal section. + +2011-05-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Added http://repository.jboss.org/maven2 repo + to fix flys/issue30 + +2011-05-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + To make diagram generation possible ws are now generated from qs + because they are many ws having different qs. + +2011-05-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + Fetch the WQ data from WINFO artifact and write those values into the + CSV export. + +2011-05-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added + methods to compute and retrieve the data for discharge curves (computed). + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Fetch the computed discharge curve data from WINFOArtifact and add the + values into the JFreeChart dataset. + +2011-05-04 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added new transitions and states to enable + the WINFO artifact for computing discharge curves. + + * doc/conf/conf.xml: Added OutGenerators that generate computed discharge + curves and exports for its data. + + * src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java: + New. This state is reached if the user chose the computed discharge + curve. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + New. This is only a stub implementation at the moment. This + OutGenerator should create computed discharge curves later. It extends + the DischargeCurveGenerator which should do the same stuff for discharge + curves for gauges. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + New. This is only a stub implementation at the moment. This OutGenerator + should create the exports of the discharge curve computation. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added a new output mode for the duration + curve state (CSV export). + + * doc/conf/conf.xml: Added a new OutGenerator to export duration curve + computations. + + * src/main/java/de/intevation/flys/exports/AbstractExporter.java: New. + This abstract OutGenerator represents the base class for exporting + computed data. Currently, the CSV export is supported. + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: Moved + the most code to export to CSV into the AbstractExporter. + + * src/main/java/de/intevation/flys/exports/DurationCurveExporter.java: + New. This exporter exports the computed data of a duration computation. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Modified a wrong debug statement which would confuse the user. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added a new output mode for the waterlevel + state (CSV export). + + * doc/conf/conf.xml: Added a new OutGenerator to export waterlevels. + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: New. + This OutGenerator exports the data of a waterlevel computation. Note: + It is necessary to specify the desired facet (e.g. + 'waterlevel_export.csv'). + + * pom.xml: Added a dependency to OpenCSV. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Introduced an "export" output target. If the target is "export", a + facet (read from the incoming xml document) is a necessary parameter + that determines which facets are written to the output. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages_en.properties: Bugfix: replaced german + string (copy & paste mistake). + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added new i18n strings for + for the location selection. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java: + New. An OutGenerator for creating duration curves. + + * src/main/java/de/intevation/flys/artifacts/model/WQDay.java: New. A + model class to store necessary data for creating W and Q facets of a + duration curve. This model stores W, Q and Days. + + * src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java: + Added a function to retrieve tuples of (day, q) based on a given gauge - + these tuples are necessary for creating duration curves. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added + methods to compute and retrieve the data necessary for creating duration + curves. + + * src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java: + Bugfix: improved the access to the location array (avoid + NullPointerException). + + * doc/conf/conf.xml: Registered the new OutGenerator for duration curves. + +2011-05-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * contrib/visualize-transitions.xsl: State quoting was done wrong. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Enhanced the transition model to reach the + final state for creating duration curves. + + * src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java: + New. This state should be reached to just insert an array of locations. + + * src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java: + New. This state is reached if the duration curve calculation is + selected. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Adjusted + getDistance() so that it takes care on inserted locations - not just + inserted ranges. + +2011-05-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java: + Flush/persist caches at program exists. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Commented out too eloquent debug output. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Uses the methods of FLYSArtifact to retrieve the necessary information + rivername and selected distance. + +2011-05-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Added interpolateW method to take reference to result ws array + as an argument to avoid expensive array allocations in km iterating + loops. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: + Added a constructor to create backing trove datastructure + with the right capacity. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Move allocation of result ws out of km loop. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added two further data items in the WQ + selection state. Those items are necessary to store the information + about the selected mode (range/single selection) and the values of the + single selection. + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: If + there is no value for a data item, this item is not written into the + static DESCRIBE part. + + * src/main/java/de/intevation/flys/artifacts/states/RangeState.java, + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Renamed the method to validate upper and lower values. + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: This + state can now handle values inserted in the single selection. Therefore, + new validate methods has been added. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The + methods getWs() and getWs() take care on the values inserted in the + single insert mode of the client which enables the user to insert single + W and Q. + +2011-05-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Repaired getQForW() by calculating indices on right dimension. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New + methods for retrieving selected W values (getWs()). + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The + method for retrieving waterlevel data takes care on selected Ws, now. + The selected Ws are transformed using the DischargeTables.getQForW() + into Q values. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Removed an unused parameter 'result' of getQForW(). + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Removed + the getDataset() method and replaced it with a addDataset() method. + On this way, concrete subclasses of this OutGenerator can have multiple + datasets (e.g. different datasets for W and Q). This abstract method is + called after the chart generation is finished. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Both classes implement the addDataset() method. The + LongitudinalSectionGenerator has already multiple datasets for W and Q. + Both are added to the chart - both have an own range axis. + +2011-05-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Use the cache for the wst value table if configured. + + * doc/conf/cache.xml: Choose a more precise name for the + wst value table cache. + +2011-04-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Added dependency to Ehcache. Apache 2.0 license. + + * doc/conf/conf.xml: Added configuration of ehcache. + + * doc/conf/cache.xml: New. Cache configurations. + + * src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java: + New. Factory to access caches. + +2011-04-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Forgot to add. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + New. Given a database connection and a XML template it generates + an output with meta data about the database. + + * src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java: + New. Holds prepared statements optimized to be run in the stack of + contextes. + + * src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java: + New. Model to hold a hierarchical scope of variables. + + * src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java: + New. Stores data set fetched from a sql select to be iterated in + a context. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java, + src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Removed superfluous imports. + +2011-04-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/conf.xml: Added meta data service. + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Stub for the meta data service. + + * src/main/resources/metadata/template.xml: Initial template for + the meta data service. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Bugfix: Now, curves for Q values are drawn into a longitudinal section + chart as well. Therefore, it was necessary to change the datastructure + of the inner class ThemeList that stores all themes included in a chart + in an ordered list (stored in a java.util.Vector now). + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: New. An + abstract base class for ChartGenerators that create XY charts. + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Sourced the generate() method out to the XYChartGenerator. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Implemented the methods to add W and Q facets to the chart. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Improved + the calculation of the step with for ranges. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Added a + method that returns the number of elements stored in the data pool. + +2011-04-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: + Added a get() method which takes destination array as an + argument. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Changed the + data structure to store w, q and kms values from List<Double> to + TDoubleArrayList which stores native double values instead of big + Double values. + + * pom.xml: Added the GNU Trove dependency. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/OutGenerator.java: Added the + name a the requested facet to doOut(). Concrete generators should just + create output for this facet now. + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Adapted the method signature of doOut(). + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adapted the method call of OutGenerator.doOut(). + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/themes.xml: Added mappings for the facets + longitudinal.section.w and longitudinal.section.q. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added some + methods to retrieve necessary information for computing the data of a + waterlevel. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added + methods to compute and retrieve the data of a waterlevel computation. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: New. This + model class represents a pool of data triples that consist of W, Q and + Kms information. This class might be used to compute data for creating + longitudinal section curves (which are based on those W, Q and Kms + values). + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Bugfix: Removed endless loop and a bug while iterating over Hibernate + results. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + New. An OutGenerator that creates longitudinal section curves. + + NOTE: This is just the stub - the out creation needs to be implemented! + + * doc/conf/conf.xml: Added the LongitudinalSectionGenerator. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Bugfix: Added missing <output-generators> section. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WaterlevelState.java: New. + This state should be reached if the 'calc.surface.curve' calculation + method has been chosen. + + * doc/conf/artifacts/winfo.xml: Modified a transition and added the + WaterlevelState. This state is reached if the 'calc.surface.curve' + calculation method has been chosen. It currently has 1 output - a + longitudinal section that is not implemented yet! + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/transitions/ValueCompareTransition.java: + New. This transition is valid if the a data object of the current + artifact equals/notequals a configured value in the transition model. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Bugfixes: + - Adapted the parameters of the isStateReachable() call - added the + artifact and the current state. + - Append the outputs of a current state if the state is filled with + valid data. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java: + Added the missing init() method that has been introduced in the + interface some commits ealier. + + * src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java: + Call init() after a Transition has been created. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + Bugfix: Introduced a <art:outputs> node in the attribute document of a + Collection that contains further <art:output> nodes - instead of having + multiple <art:output> nodes at toplevel of the document. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adapted the XPath to retrieve the outputs in the attribute document of a + Collection. + +2011-04-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * contrib/visualize-transitions.xsl: Added to create a + Graphviz digraph out of the config.xml. Usage: + + $ xsltproc --stringparam base-dir ../doc/conf/ \ + contrib/visualize-transitions.xsl \ + doc/conf/conf.xml > transitions.dot + + $ dot -Tsvg -o transitions.svg transitions.dot + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + If no data has been inserted so far, an IllegalArgumentException is + thrown. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:: + A lot of new methods to retrieve the theme of a facet - used while + creating the output of a facet/artifact. If a facet has no theme yet, it + is initialized. + +2011-04-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/AttributeParser.java, + src/main/java/de/intevation/flys/collections/OutputParser.java: Removed + useless imports. + +2011-04-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: New. An + abstract OutGenerator that might be used to create chart output. Some + basic things that are equal in all charts should be done here! + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + This OutGenerator no longer implements the OutGenerator directly, but it + extends the ChartGenerator now. + +2011-04-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Restructured the out() operation of a collection: Now, we collect a list + of artifacts/facets and use this for the OutGenerator. Its doOut() + method gets the attribute of an artifact - the position and the active + state is managed by the Collection itself. + +2011-04-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java: New. + a specialized facet that stores information about its position and its + state (active/inactive) in an output of a collection. + + * src/main/java/de/intevation/flys/collections/AttributeParser.java: New. + This parser takes the attributes (XML) of a collection and extracts the + contained outputs with its facets. The result is a Map<String, Output>. + + * src/main/java/de/intevation/flys/collections/OutputParser.java: New. + This parser is used to query the artifact's DESCRIBE and to extract the + supported outputs. The result is a Map<String, Output>. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: New. + This writer merges the outputs contained in an attribute of a collection + with the outputs of a collection's artifacts. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + The attributes of a collection are written into its DESCRIBE document + now. The OutputParser and AttributeParser are used to read the supported + attributes by the collection and its artifacts - the AttributeWriter is + used to merge both attributes and create a final attribute document. + +2011-04-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java, + src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + The facet-2-theme mappings are initialized at startup and stored in the + FLYSContext. + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: Added a + function that retrieves a theme from FLYSContext based on its name. + +2011-04-22 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Corrected a silly c&p mistake. + +2011-04-22 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + + Implementation of the "Ruecksprungkorrektur" to be done in + "W fuer angepassten Abflusslaengschnitt". + + All tests show the expected results. In some corner cases the + algorithm described in the "Anwenderhandbuch" chapter 3.3.4.3 "Korrektur" + has some definition shortcomings: + + a - What should happend when you cannot find point 2 because + you cannot step back one quarter from point 3 because there + is no data there any more (river too short in this direction)? + The implemented algorithm raises point 3' only to an + according factor. E.g. If you can step back the whole quarter + distance the elevation is the full quarter. If you can + step back only the half of the quarter the elevation is + only an eighth. + + b - If the water heights between point 2 and 3 are constant then + the algorithm will produce a spline interpolation that + lowers those values. Is this intended? + + For real data the back jumps are expected to be more in the middle + of the distance ranges so the corner cases are maybe not so + important. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Removed superfluous import. + +2011-04-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: Removed debug + code that has been commited by accident. + +2011-04-21 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/themes.xml: New. A first small theme configuration. + + * doc/conf/conf.xml: Added a link to the theme configuration. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: + Defined a key that is used to store a themes map in the FLYSContext. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + The theme configuration is read at startup and the themes are stores in + the FLYSContext. + +2011-04-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/themes/Theme.java, + src/main/java/de/intevation/flys/themes/DefaultTheme.java: + New. The interface and its default implementation that represents themes + used to style charts and maps. + + * src/main/java/de/intevation/flys/themes/ThemeField.java, + src/main/java/de/intevation/flys/themes/DefaultThemeField.java: + New. The interface and its default implementation that represents fields + in themes. A theme might be "Lines" and one of its field might be + "Color" or "Size". + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: A factory + that creates new themes based on a theme configuration. + +2011-04-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Implementation of "Abflusskurve/Abflusstafel" calculation. + + Added method interpolateWQ() which takes an km and results in a + tuple of two double arrays containing the w/q values interpolated + between the surrounding w/q values of the table. + w values are interpolated linear, q values with a cubic spline. + + Drawing w over q gives you the discharge table at the given km. + + !!! This code needs testing !!! + +2011-04-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Added dependency to Apache Commons Math 2.2 (Apache License 2.0) + +2011-04-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Fix problem when sorting by q (copied w instead of q). + +2011-04-20 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Fixed broken HQL statement. + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + add a method interpolateW() which takes an array of + q values and returns an equal sized array of w values. + This is essentially the "Wasserstand/Wasserspiegellagen" calculation + of desktop FLYS. + + If you want to do a calculation with given w values you have + to convert the w values with DischargeTables.getQForW() first. + + !!! This code needs heavy testing !!! + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + We need a getQForW() method and not getWForQ() because when + doing a "Wasserstand/Wasserspiegellagen" calculation with given + w values these values need to be translated to q values with + the master discharge table. + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Sorting of q values was done wrong. + +2011-04-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java: + Ordered the list of annotations returned by this factory based on its + range. + +2011-04-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java: + New. This service returns an XML document that includes the main values + of a gauge based on a river name, a start and an end point. + + * doc/conf/conf.xml: Registered the MainValuesService. + +2011-04-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java: + A factory that provides methods to return MainValues. + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Added static method getWForQ() to interpolate a w value for + a given q value based on a given discharge table. + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Added convenience constructors/methods to ease the access to the master + discharge table of a gauge. + +2011-04-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Moved the query complexity into view 'wst_value_table' and + used this instead. + +2011-04-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Fetches w/q value tables from the backend. TODO: Move this + to the backend and use a view. + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Changed + the error key that is thrown if no input data was found so that the key + is usable for GWT's i18n mechanism. + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + The inserted river is validated now (overrides validate() of + DefaultState). + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + The inserted calculation method is validated now (overrides validate() + of DefaultState). + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + The input data of feed() are validated using the DefaultStates + validate() method. + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + New method validate() that needs to be overidden by concrete subclasses. + It should return true, if the data of the State is fine, otherwise it + should raise an exception. + + NOTE: The exceptions are not translated in the server but in the client! + + * src/main/java/de/intevation/flys/artifacts/states/RangeState.java: New. + This abstract class exists to provide some methods for handling ranges. + Currently, there is a method that validates a given range based on + min/max values. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + Implemented input data validation for ranges. + + NOTE: The input validation of concrete values has not been implemented + yet! + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Removed debug code that has been commited by accident :-/ + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The + getGauge() method returns the first gauge based on the given start and + end point of the river. + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + Fixed potential bugs: if no gauge could be determined, the default + values for W and Q are the minimum and maximum double values. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: Fills + the DESCRIBE with default values for W and Q. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstFactory.java: + New. Returns Wst object - based on a river. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Now + provides some methods that return some basic objects inserted while + parameterization: River, Gauge and so on. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java, + src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + Bugfix: Repaired broken imports of the SessionHolder. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java: + Moved this class to flys-backend. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java, + src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java, + src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java, + src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java, + src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java: + Adapted imports of the SessionHolder. + +2011-04-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * trunk/src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java, + trunk/src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + Acquire/release sessions in services to avoid db connection leaks. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Changed some method signatures - added a reference to the owner + Artifact. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java, + src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Modified method signatures based on the changes in DefaultState. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Added default values to the dynamic part of DESCRIBE. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Added a function that returns a River object based on a given river + name. + +2011-04-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java, + src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java, + src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java, + src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java: + Static methods are using the SessionHolder, too. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java: + Added the setup() method that has been added to the interface + description in the last commits. + + * doc/conf/conf.xml: Registered the SessionCallContextListener as + CallContext.Listener. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java: + Bugfix: Call correct method to retrieve an instance of + SessionFactoryProvider. + +2011-04-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java: + New. Contains thread local session holder for hibernate sessions. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Uses session from SessionHolder. + + * src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java: + Interacts with SessionHolder now. + + * src/main/java/de/intevation/flys/exports/ChartExportHelper.java: + Removed superfluous import. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java: + Implementation of a CallContext.Listener to open/close Hibernate + Sessions for each request. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Some modifications related to the last commit - modification of the + describe() signature of a State. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java: + Removed. We do not need a state to select a gauge - the selection takes + place by choosing a start and an end point. + +2011-04-13 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Bugfix: added missing label node to root node and a namespace to the + data node. + +2011-04-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + The name of the artifact 'winfo' is written into the DESCRIBE document + now. We need this to have a proper way to distinguish between different + artifacts in the UI. + +2011-04-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Bugfix: + States are filled with data before they describe themself. + +2011-04-11 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java: + New. A factory that returns the annotations of a specific river. + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java: + New. This service provides a document that contains information about + distances of a river. + + * doc/conf/conf.xml: Registered the DistanceInfoService. + +2011-04-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + This collection overrides the out() operation now. The incoming request + document is read and the related OutGenerator is used to create the + output. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Removed + the code to create discharge curves. It has moved to the + DischargeCurveGenerator which now does this work. + +2011-04-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/OutGenerator.java: The + generate() method throws an IOException now. + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + New. This OutGenerator creates discharge curves. + +2011-04-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Changed + the visibility of the getData() method. It's now public, because the + OutGenerator needs an artifact's data. + +2011-04-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/**/*.java: Removed trailing whitespace. + +2011-04-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Make project compilable again by + commenting out a not existing XMLDebug reference. + +2011-03-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Added code to parse the configured OutGenerators and to save them (in a + map) in the FLYSContext. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: + Added a key that is used to save the OutGenerators Map in the context. + +2011-03-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/OutGenerator.java: + New. This interface is used to generator different types of output. + ArtifactCollections will make use of this interface to create a + collected output of all its artifacts. + +2011-03-30 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Removed the Backend from FLYSArtifactCollection - used ArtifactDatabase + operations instead. + + * TODO: Removed 'remove Backend reference' TODO. + +2011-03-30 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Enhanced the configuration of the + discharge curve output mode. This output now provides three facets - W, + Q and the curve itself. + +2011-03-30 Ingo Weinzierl <ingo@intevation.de> + + Tagged RELEASE 0.1 + +2011-03-30 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Renamed an output mode in the WINFO + artifact configuration. + +2011-03-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages_de_DE.properties, + src/main/resources/messages_de.properties: Changed a german string. + +2011-03-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Modified the available calculation modes and its order in the DESCRIBE + document. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Adapted the names of + calculation modes. + +2011-03-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Added the hash code of an artifact to the artifact part of the + collection's DESCRIBE document. + +2011-03-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Changed + the background color of discharge curves to white. + +2011-03-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Cache the scale, too. Otherwise two calls to getValues() with + different arguments will result in the same output. + +2011-03-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Introduced a 'scale' parameter in the getValues() method. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Filled + the out() operation with code that draws a discharge table of one or + more gauges specified by the given range in entered in a previous state. + +2011-03-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartExportHelper.java: + New. A helper class to exports charts. + + * pom.xml: Added dependencies to iText, Batik and JFreeChart. + +2011-03-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/importer/PegelGltParser.java: + Fixed swap of operands. + +2011-03-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java: + New. Load gauges for a river and filter them for given + ranges. + +2011-03-25 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Store data in a double [][] instead of interleaved double [] + to be compatible with org.jfree.data.xy.DefaultXYDataset. + +2011-03-25 Ingo Weinzierl <ingo@intevation.de> + + * TODO: Removed 'i18n' and 'step-back' TODOs and added an issue to remove + the Backend reference from FLYSArtifactCollection. + +2011-03-25 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Added missing artifact namespace of an attribute in the DESCRIBE + document. + +2011-03-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + New. This ArtifactCollection overrides the DefaultArtifactCollection to + implement FLYS specific describe() and out() operations. + + * doc/conf/conf.xml: Use the FLYSArtifactCollection instead of the + DefaultArtifactCollection for this application. + +2011-03-24 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + New. Fetches values of discharge tables in form of packed + w/q double arrays for given gauges. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Removed needless import. + +2011-03-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages_de_DE.properties: Added a german resource + bundle to avoid exceptions in the flys artifacts. Sometimes, the + Resources class is not able to find a 'de_DE' bundle and throws an + exception. This is really strange, because it should use the 'de' bundle + in that case, but it doesn't. + +2011-03-23 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added + some code to append the output modes of previous states to the DESCRIBE + document. + + TODO: Determine if the current state is already filled with data and + append its output modes as well! + +2011-03-22 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Enhanced the location_distance state with + an output mode 'discharge_table'. + +2011-03-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Write human readable strings as label attribute into the DESCRIBE + output. Those labels are used to be displayed in the GUI. + +2011-03-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Added some further calculation types. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n strings for the + calculation types + +2011-03-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Implemented the step-back part of the advance() operation. + +2011-03-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages_de.properties: Fixed a german umlaut. + +2011-03-18 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added new states for entering a + location/distance and w/q. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: New string for the + location/distance and w/q input states. + +2011-03-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + New. A state for the W/Q input of the WINFO parameterization. + +2011-03-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Append an attribute 'uiprovider' to the dynamic UI node. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + New. A state for the location/distance selection of the WINFO + parameterization. + +2011-03-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Added a new method getUIProvider() that might be overriden by concreted + subclasses that should be rendered with a specific UIProvider. + +2011-03-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + The static UI part is created by the previous states now. This makes it + possible to group the data objects (which is necessary to group the + objects in the ui). + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Added a describeStatic() method that creates a node that contains the + data of that state. + +2011-03-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + Removed TODO: the document contains the rivers provided by the backend + now. + +2011-03-17 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Added dependency to flys backend. + + * src/main/java/de/intevation/flys/artifacts/model/River.java: + Removed. We are using the backend model now. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Fetches the rivers from the backend. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Import fixes. + +2011-03-15 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/conf.xml: Added section for database backend configuration. + +2011-03-15 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Fixed build error coming from different import of XMLUtils. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Appended the missing label node that contains the human readable name of + the data item. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + I18N of strings for the DESCRIBE document. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: I18N strings for the + calculcation mode state. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Modified the winfo states. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Adapted + the artifact regarding the changes of the last commit. The states + describe() method creates the dynamic UI node - the artifact needs to + apply this node. + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + New. The state for choosing the calculation mode. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n strings for the + calculation mode state. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + New. This is the base state for the FLYS application. It provides a + method that creates the dynamic ui node for the DESCRIBE. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java: Both + classes extend the abstract base class DefaultState. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/resources/Resources.java: + New. This class retrieves the i18n strings from a ResourceBundle. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Resource files for german and + english translation. + +2011-03-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Appended + the data that have been inserted in former states into the static node + of the DESCRIBE. + +2011-03-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The + operations feed() and advance() return the description of the artifact + using the describe() operation. This avoids additional server round trips + in the client - the clients gets to know about the new state of the + artifact immediately. + +2011-03-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Implemented a part (step forward) of the advance operation. + + * TODO: Implement Step-Back in advance operation. + +2011-03-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Implemented the abstract method getName(). It returns the constant + 'winfo' string. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Implemented the feed action. The data of an incoming feed() operation is + stored in StateData objects that are saved in a map in the artifact. + + NOTE: There is no input validation and no i18n of error messages (see + TODO). + +2011-03-09 Ingo Weinzierl <ingo@intevation.de> + + * TODO: This file contains some open points that need to be done. + +2011-03-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New. This + artifact serves as the default artifact for the FLYS application. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: This + artifact now inherits from FLYSArtifact. Furthermore, there is one big + change: we don't store the State objects itself in the artifact, but + just the identifier of those. This makes the artifact smaller and more + compatible agains previous versions of the software. + +2011-03-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + New. This service will retrieve a list of provided rivers. + + * doc/conf/conf.xml: Added a configuration for the RiverService. + +2011-03-07 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Added a section user-factory and collection-factory in + the factories part of the configuration. + +2011-03-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Replaced the 'special' attribute from DESCRIBE with a 'uiprovider' + attribute. + +2011-02-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The + RiverSelect state is called to create the UI part of the describe + document. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Implemented the dynamic UI part of describe(). The static part is not + inserted into the describe document at the moment. We need a reference to + the previous states for this. + +2011-02-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java, + src/main/java/de/intevation/flys/artifacts/model/River.java: New. A model + class that represents a river and its factory to create concrete river + instances. + NOTE: Currently, this is just a mockup. The factory just returns two + static rivers "Mosel" and "Saar" without a connection to a backend. + +2011-02-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/StateFactory.java: The + input data of a state is initialized with empty StateData objects after + the State has been created. + + * doc/conf/artifacts/winfo.xml: Renamed the input data nodes of the states + which now fits better to the class name of the implementation. + +2011-02-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The + describe document returned by this artifact now contains the current state + and the reachable states. + +2011-02-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + The transitions are put into the TransitionEngine with the ID of the state + - not longer with the artifact name. On this way, we are able to fetch + just the transitions for a specific state, instead of all the transitions + of an artifact. + +2011-02-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Describe() + returns the artifact's uuid and hash value. The whole implementation of + describe() is still outstanding. + +2011-02-04 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Removed useless config stuff. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Removed + useless methods, and improved the init process - the first state is set as + the current state for this artifact. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java: New. + The states are used in the first two steps of the WINFOArtifact. + Currently, they just implement stubs of the necessary methods setup() and + describe(). + +2011-02-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/State.java, + src/main/java/de/intevation/flys/artifacts/transitions/TransitionEngine.java, + src/main/java/de/intevation/flys/artifacts/transitions/Transition.java: + Removed. These classes are placed in the artifact-database now. + + * src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java, + src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java: + Adapted imports of Transition. + + * src/main/java/de/intevation/flys/artifacts/states/StateFactory.java: New. + This factory should be used to create concrete State objects. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: Added + a constant key to store the StateEngine in the context. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + New method that initializes the states at application start. + +2011-02-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/State.java: New. The + interface description of a state. + + * src/main/java/de/intevation/flys/artifacts/transitions/Transition.java, + src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java: + New. The interface description and a default implementation of a + transition. + + * src/main/java/de/intevation/flys/artifacts/transitions/TransitionEngine.java: + New. The TransitionEngine stores all transitions for each artifact and + should be used to determine, if an artifact can advance from one state to + another. + + * src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java: + New. Transitions should be created by using this class. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: New. + The Flys context. It currently defines keys to store important components + in the context. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + New. The context factory initializes the basic components of the + application. Currently, the TransitionEngine is created and all artifacts + with its transitions are read from the global configuration and stored in + the FLYSContext. + + * pom.xml: Added a dependency to the 'artifacts-common' package. + + * doc/conf/artifacts/winfo.xml: Corrected the classname of the + DefaultTransition. + + * doc/conf/conf.xml: Added FLYSContextFactory as context-factory. + +2011-02-02 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: An initial configuration file for the FLYS artifact + server. + + * doc/conf/artifacts/winfo.xml: An initial transition configuration of an + WINFO artifact. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: A stub of + an artifact for a WINFO parameterization. + + * pom.xml: Set the source code version to 1.5. + +2011-02-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/**, pom.xml: Added initial maven project. + * ChangeLog: new.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/Changes Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,59 @@ +2011-05-13 RELEASE 2.3 + + NEW: + + * Initial release of the artifacts for FLYS. Currently there is a single + WINFO artifact for the following computations: + - waterlevels + - discharge curves + - duration curves + - discharge longitudinal section curves + + * Configuration is placed in doc/conf/conf.xml + + * WINFO Artifact specific configuration is placed in + doc/conf/artifacts/winfo.xml + + * So called 'OutGenerators' produce different types of output. + Currently, the flys-artifacts are able to produce charts and exports. + Each output type has to be configured in conf.xml. + + * New chart types: + - discharge curves (dt. 'Abflusskurven am Pegel') + - computed discharge curves (dt. 'Abflusskurve') + - longitudinal section curves (dt. 'Längsschnitte') + - duration curves (dt. 'Dauerlinie') + - discharge longitudinal section curve (dt. 'W bei + ungleichmäßigem Abflusslängsschnitt') + + * New exports: + - csv of waterlevels + - csv of duration curves + - csv of computed discharge curves + - csv of discharge longitudinal section + + * New services that provides: + - supported rivers + - main values of a gauge + - range information of a river + - meta information of a river + + * Caching of computation relevant values + + * Initial model to support chart specific themes (theme.xml) + + + LIMITATIONS: + + * Charts are not rendered using the themes in theme.xml + + + !!! + + The version number of this release depends on an existing desktop variant of + this software that is in version 2.1. + + !!! + + +2011-03-30 RELEASE 0.1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/NEWS Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,9 @@ +NEWS + +for enduser-specific information, c.f. the NEWS -file in the module flys-client + +2011-05-16 Release V 2.3 + +2011-03-27 Release V 2.2 + +This file starts with V. 2.2. Earlier versions are based on a Desktop-version until V 2.1.3.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/TODO Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,1 @@ +- Validation of the input values of an incoming feed() call
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/contrib/visualize-transitions.xsl Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + Copyright (c) 2010 by Intevation GmbH + + This program is free software under the LGPL (>=v2.1) + Read the file LGPL.txt coming with the software for details + or visit http://www.gnu.org/licenses/ if it does not exist. + + Author: Sascha L. Teichmann (sascha.teichmann@intevation.de) +--> + +<xsl:stylesheet + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0"> + + <xsl:output method="text" encoding="UTF-8"/> + + <xsl:param name="base-dir">.</xsl:param> + + <xsl:template match="/"> + <xsl:text>digraph transition_model {
</xsl:text> + <xsl:apply-templates /> + <xsl:text>}
</xsl:text> + </xsl:template> + + <xsl:template match="artifact"> + <xsl:choose> + <xsl:when test="@xlink:href != ''"> + <!-- handle external artifacts --> + <xsl:variable name="path"> + <xsl:call-template name="string-replace-all"> + <xsl:with-param name="text" select="@xlink:href" /> + <xsl:with-param name="replace">${artifacts.config.dir}</xsl:with-param> + <xsl:with-param name="by" select="$base-dir" /> + </xsl:call-template> + </xsl:variable> + <xsl:for-each select="document($path)"> + <xsl:apply-templates select="/artifact"/> + </xsl:for-each> + </xsl:when> + <xsl:otherwise> + <!-- handle internal artifacts --> + <xsl:text>subgraph </xsl:text><xsl:value-of select="@name"/> + <xsl:text> {
</xsl:text> + <xsl:text> label = "Artefakt: </xsl:text> + <xsl:value-of select="@name"/> + <xsl:text>";
</xsl:text> + <xsl:apply-templates mode="inside-artifact" select="./states/state"/> + <xsl:apply-templates mode="inside-artifact" select="./states/transition"/> + <xsl:text>}
</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template match="state" mode="inside-artifact"> + <xsl:text> "</xsl:text> + <xsl:value-of select="@id"/> + <xsl:text disable-output-escaping="yes" + >" [ shape = "record" label=<<table border="0" cellborder="0" cellpadding="3"> + <tr><td align="center" colspan="2" bgcolor="black"><font color="white"></xsl:text> + <xsl:value-of select="@id"/> + <xsl:text disable-output-escaping="yes" + ></font></td></tr></xsl:text> + <xsl:apply-templates mode="inside-artifact" /> + <xsl:text disable-output-escaping="yes" + ></table>>]</xsl:text> + <xsl:text>;
</xsl:text> + </xsl:template> + + <xsl:template match="data" mode="inside-artifact"> + <xsl:text disable-output-escaping="yes" + ><tr><td align="right"></xsl:text> + <xsl:value-of select="@name"/> + <xsl:text disable-output-escaping="yes" + ></td><td align="left"></xsl:text> + <xsl:value-of select="@type"/> + <xsl:text disable-output-escaping="yes" + ></td></tr></xsl:text> + </xsl:template> + + <xsl:template match="transition" mode="inside-artifact"> + <xsl:text> "</xsl:text> + <xsl:value-of select="from/@state"/> + <xsl:text disable-output-escaping="yes">" -> "</xsl:text> + <xsl:value-of select="to/@state"/> + <xsl:text>"</xsl:text> + <xsl:apply-templates mode="inside-artifact"/> + <xsl:text>;
</xsl:text> + </xsl:template> + + <xsl:template match="condition" mode="inside-artifact"> + <xsl:text> [ label="</xsl:text> + <xsl:value-of select="@inputvalue"/> + <xsl:text> </xsl:text> + <xsl:value-of select="@operator"/> + <xsl:text> </xsl:text> + <xsl:value-of select="@value"/> + <xsl:text>" ]</xsl:text> + </xsl:template> + + <xsl:template match="text()" mode="inside-artifact"/> + <xsl:template match="text()"/> + + <xsl:template name="string-replace-all"> + <xsl:param name="text" /> + <xsl:param name="replace" /> + <xsl:param name="by" /> + <xsl:choose> + <xsl:when test="contains($text, $replace)"> + <xsl:value-of select="substring-before($text,$replace)" /> + <xsl:value-of select="$by" /> + <xsl:call-template name="string-replace-all"> + <xsl:with-param name="text" + select="substring-after($text,$replace)" /> + <xsl:with-param name="replace" select="$replace" /> + <xsl:with-param name="by" select="$by" /> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$text" /> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + +</xsl:stylesheet> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/artifacts/winfo.xml Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,187 @@ +<?xml version="1.0" encoding="UTF-8"?> +<artifact name="winfo"> + <states> + + <state id="state.winfo.river" description="state.winfo.river" state="de.intevation.flys.artifacts.states.RiverSelect"> + <data name="river" type="String" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.river"/> + <to state="state.winfo.calculation_mode"/> + </transition> + + <state id="state.winfo.calculation_mode" description="state.winfo.calculation_mode" state="de.intevation.flys.artifacts.states.CalculationSelect"> + <data name="calculation_mode" type="String" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.location_distance"/> + <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.location"/> + <condition data="calculation_mode" value="calc.discharge.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.location"/> + <condition data="calculation_mode" value="calc.duration.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.location_distance"/> + <condition data="calculation_mode" value="calc.discharge.longitudinal.section" operator="equal"/> + </transition> + + <state id="state.winfo.location" description="state.winfo.location" state="de.intevation.flys.artifacts.states.LocationSelect"> + <data name="ld_locations" type="Double[]" /> + + <outputmodes> + <outputmode name="discharge_curve" description="output.discharge_curve" mime-type="image/png"> + <facets> + <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/> + <facet name="discharge_curve.w" description="facet.discharge_curve.w"/> + <facet name="discharge_curve.q" description="facet.discharge_curve.q"/> + </facets> + </outputmode> + </outputmodes> + </state> + + <state id="state.winfo.location_distance" description="state.winfo.location_distance" state="de.intevation.flys.artifacts.states.LocationDistanceSelect"> + <data name="ld_from" type="Double" /> + <data name="ld_to" type="Double" /> + <data name="ld_step" type="Double" /> + + <outputmodes> + <outputmode name="discharge_curve" description="output.discharge_curve" mime-type="image/png"> + <facets> + <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/> + <facet name="discharge_curve.w" description="facet.discharge_curve.w"/> + <facet name="discharge_curve.q" description="facet.discharge_curve.q"/> + </facets> + </outputmode> + </outputmodes> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.location_distance"/> + <to state="state.winfo.wq"/> + <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.location_distance"/> + <to state="state.winfo.wq_adapted"/> + <condition data="calculation_mode" value="calc.discharge.longitudinal.section" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.location"/> + <to state="state.winfo.durationcurve"/> + <condition data="calculation_mode" value="calc.duration.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.location"/> + <to state="state.winfo.computeddischargecurve"/> + <condition data="calculation_mode" value="calc.discharge.curve" operator="equal"/> + </transition> + + <state id="state.winfo.wq" description="state.winfo.wq" state="de.intevation.flys.artifacts.states.WQSelect"> + <data name="wq_mode" type="String" /> + <data name="wq_selection" type="String" /> + <data name="wq_from" type="Double" /> + <data name="wq_to" type="Double" /> + <data name="wq_step" type="Double" /> + <data name="wq_single" type="Double[]" /> + </state> + + <state id="state.winfo.wq_adapted" description="state.winfo.wq_adapted" state="de.intevation.flys.artifacts.states.WQAdapted"> + <!-- TODO Add data objects --> + <data name="wq_mode" type="String" /> + <data name="wq_values" type="WQTriple" /> + </state> + + <state id="state.winfo.durationcurve" description="state.winfo.durationcurve" state="de.intevation.flys.artifacts.states.DurationCurveState"> + <outputmodes> + <outputmode name="duration_curve" description="output.duration_curve" mime-type="image/png"> + <facets> + <facet name="duration_curve.w" description="facet.duration_curve.w"/> + <facet name="duration_curve.q" description="facet.duration_curve.q"/> + </facets> + </outputmode> + <outputmode name="durationcurve_export" description="output.waterlevel_export" mime-type="text/plain"> + <facets> + <facet name="csv" description="facet.durationcurve_export.csv" /> + </facets> + </outputmode> + </outputmodes> + </state> + + <state id="state.winfo.computeddischargecurve" description="state.winfo.computeddischargecurve" state="de.intevation.flys.artifacts.states.ComputedDischargeCurveState"> + <outputmodes> + <outputmode name="computed_discharge_curve" description="output.computed_discharge_curve" mime-type="image/png"> + <facets> + <facet name="computed_discharge_curve.w" description="facet.computed_discharge_curve.w"/> + <facet name="computed_discharge_curve.q" description="facet.computed_discharge_curve.q"/> + </facets> + </outputmode> + <outputmode name="computed_dischargecurve_export" description="output.computed_dischargecurve_export" mime-type="text/plain"> + <facets> + <facet name="csv" description="facet.computed_dischargecurve_export.csv" /> + </facets> + </outputmode> + </outputmodes> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.wq"/> + <to state="state.winfo.waterlevel"/> + <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.wq_adapted"/> + <to state="state.winfo.discharge_longitudinal_section"/> + </transition> + + <state id="state.winfo.waterlevel" description="state.winfo.waterlevel" state="de.intevation.flys.artifacts.states.WaterlevelState"> + <outputmodes> + <outputmode name="longitudinal_section" description="output.longitudinal_section" mime-type="image/png"> + <facets> + <facet name="longitudinal_section.w" description="facet.longitudinal_section.w"/> + <facet name="longitudinal_section.q" description="facet.longitudinal_section.q"/> + </facets> + </outputmode> + <outputmode name="waterlevel_export" description="output.waterlevel_export" mime-type="text/plain"> + <facets> + <facet name="csv" description="facet.waterlevel_export.csv" /> + </facets> + </outputmode> + </outputmodes> + </state> + + <state id="state.winfo.discharge_longitudinal_section" description="state.winfo.discharge_longitudinal_section" state="de.intevation.flys.artifacts.states.DischargeLongitudinalSection"> + <outputmodes> + <outputmode name="discharge_longitudinal_section" description="output.discharge_longitudinal_section" mime-type="image/png"> + <facets> + <facet name="discharge_longitudinal_section.w"/> + <facet name="discharge_longitudinal_section.q"/> + </facets> + </outputmode> + <outputmode name="discharge_longitudinal_section_export" description="output.discharge_longitudinal_section_export" mime-type="text/plain"> + <facets> + <facet name="csv" description="facet.discharge_longitudinal_section_export.csv" /> + </facets> + </outputmode> + </outputmodes> + </state> + + </states> +</artifact>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/cache.xml Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ehcache> + + <diskStore path="java.io.tmpdir"/> + + <defaultCache + maxElementsInMemory="1000" + eternal="false" + timeToIdleSeconds="120" + timeToLiveSeconds="3600" + overflowToDisk="true" + maxElementsOnDisk="100000" + diskPersistent="false" + diskExpiryThreadIntervalSeconds="120" + memoryStoreEvictionPolicy="LRU" + /> + + <!-- This one is used for the WST value tables --> + + <cache name="wst-value-table" + maxElementsInMemory="20" + maxElementsOnDisk="100" + eternal="false" + timeToIdleSeconds="360" + overflowToDisk="true" + timeToLiveSeconds="14400" + diskPersistent="true" + memoryStoreEvictionPolicy="LRU" + /> +</ehcache>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/conf.xml Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,100 @@ +<artifact-database> + <export-secret>YOUR_SECRET</export-secret> + <factories> + <context-factory>de.intevation.flys.artifacts.context.FLYSContextFactory</context-factory> + + <collection-factory + name="DefaultArtifactCollectionFactory" + description="The default artifact collection factory" + ttl="60000" + artifact-collection="de.intevation.flys.collections.FLYSArtifactCollection">de.intevation.artifactdatabase.DefaultArtifactCollectionFactory</collection-factory> + + <artifact-factories> + <!-- All Artifactfactories which are available in this Database. + Each Artifactfactory represents one river. --> + <artifact-factory name="winfo" description="Factory to create an artifact to be used in WINFO" + ttl="3600000" + artifact="de.intevation.flys.artifacts.WINFOArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + </artifact-factories> + + <user-factory name="default" description="Factory to create new users">de.intevation.artifactdatabase.DefaultUserFactory</user-factory> + + <service-factories> + <service-factory + name="rivers" + service="de.intevation.flys.artifacts.services.RiverService" + description="This service returns a list of provided rivers by the artifact server.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + <service-factory + name="distanceinfo" + service="de.intevation.flys.artifacts.services.DistanceInfoService" + description="Returns a list of distances supported by a specific river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + <service-factory + name="mainvalues" + service="de.intevation.flys.artifacts.services.MainValuesService" + description="Returns the main values of a river's gauge based on a start and end point of the river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + <service-factory + name="metadata" + service="de.intevation.flys.artifacts.services.MetaDataService" + description="The service provides some introspection into the database content.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + </service-factories> + + </factories> + + <callcontext-listener + name="SessionCallContextListener" + description="A CallContext.Listener to open and close Hibernatesessions">de.intevation.flys.artifacts.context.SessionCallContextListener</callcontext-listener> + + <artifacts> + <artifact name="winfo" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/winfo.xml" /> + </artifacts> + + <output-generators> + <output-generator name="discharge_curve">de.intevation.flys.exports.DischargeCurveGenerator</output-generator> + <output-generator name="computed_discharge_curve">de.intevation.flys.exports.ComputedDischargeCurveGenerator</output-generator> + <output-generator name="longitudinal_section">de.intevation.flys.exports.LongitudinalSectionGenerator</output-generator> + <output-generator name="duration_curve">de.intevation.flys.exports.DurationCurveGenerator</output-generator> + <output-generator name="discharge_longitudinal_section">de.intevation.flys.exports.DischargeLongitudinalSectionGenerator</output-generator> + <output-generator name="waterlevel_export">de.intevation.flys.exports.WaterlevelExporter</output-generator> + <output-generator name="durationcurve_export">de.intevation.flys.exports.DurationCurveExporter</output-generator> + <output-generator name="computed_dischargecurve_export">de.intevation.flys.exports.ComputedDischargeCurveExporter</output-generator> + <output-generator name="discharge_longitudinal_section_export">de.intevation.flys.exports.DischargeLongitudinalSectionExporter</output-generator> + </output-generators> + + <rest-server> + <!-- at this Point it is possible to Change the Port the ArtifactDatabase will bind to.--> + <port>8181</port> + <listen>localhost</listen> + </rest-server> + <!-- garbage collection of outdated artifacts --> + <cleaner> + <sleep-time>60000</sleep-time> + </cleaner> + + <cache> + <config-file>${artifacts.config.dir}/cache.xml</config-file> + </cache> + + <database> + <!-- This Section configures the Settings for connecting to the + Artifact-Database instance. e.g. SQLite --> + <user>SA</user> + <password></password> + <url>jdbc:h2:${artifacts.config.dir}/../artifactdb/artifacts.db</url> + </database> + <!-- This is the default backend db configuration --> + <!-- + <backend-database> + <user>flys</user> + <password>flys</password> + <dialect>org.hibernate.dialect.PostgreSQLDialect</dialect> + <driver>org.postgresql.Driver</driver> + <url>jdbc:postgresql://localhost:5432/flys</url> + </backend-database> + --> + + <flys> + <themes> + <configuration>${artifacts.config.dir}/themes.xml</configuration> + </themes> + </flys> +</artifact-database>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/themes.xml Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,62 @@ +<themes> + + <!-- Concrete themes are following now! --> + <theme name="DischargeCurve"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + </theme> + + <theme name="LongitudinalSectionW"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + </theme> + + <theme name="LongitudinalSectionQ"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + </theme> + + + + <!-- Virtual themes are following now! --> + <theme name="Lines" type="virtual"> + <fields> + <field name="showlines" type="boolean" display="Linie anzeigen" default="true"/> + <field name="linesize" type="int" display="Liniendicke" default="1"/> + <field name="linetype" type="Dash" display="Linienart" default="new Dash()"/> + </fields> + </theme> + + <theme name="ColorLines" type="virtual"> + <inherits> + <inherit from="Lines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="Color.BLACK"/> + </fields> + </theme> + + <theme name="HiddenColorLines" type="virtual"> + <inherits> + <inherit from="ColorLines"/> + </inherits> + <fields> + <field name="showlines" type="boolean" display="Linie anzeigen" default="true" hints="h"/> + <field name="linesize" type="int" display="Liniendicke" default="1" hints="h"/> + <field name="linetype" type="Dash" display="Linienart" default="new Dash()" hints="h"/> + </fields> + </theme> + + + + <!-- Mappings are following now. A mapping maps between a name of a facet + and a theme. --> + <mappings> + <mapping from="discharge_curve.curve" to="DischargeCurve"/> + <mapping from="longitudinal_section.w" to="LongitudinalSectionW"/> + <mapping from="longitudinal_section.q" to="LongitudinalSectionQ"/> + </mappings> +</themes>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/pom.xml Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,106 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>de.intevation.flys.artifacts</groupId> + <artifactId>flys-artifacts</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>flys-artifacts</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.0.2</version> + <configuration> + <source>1.5</source> + <target>1.5</target> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>net.sf.ehcache</groupId> + <artifactId>ehcache-core</artifactId> + <version>2.4.2</version> + </dependency> + <dependency> + <groupId>jfree</groupId> + <artifactId>jfreechart</artifactId> + <version>1.0.13</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>trove</groupId> + <artifactId>trove</artifactId> + <version>2.1.1</version> + </dependency> + <dependency> + <groupId>net.sf.opencsv</groupId> + <artifactId>opencsv</artifactId> + <version>2.0</version> + </dependency> + <dependency> + <groupId>de.intevation.bsh.artifact-database</groupId> + <artifactId>artifact-database</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>de.intevation.artifacts.common</groupId> + <artifactId>artifacts-common</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>de.intevation.flys</groupId> + <artifactId>flys-backend</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>jfree</groupId> + <artifactId>jfreechart</artifactId> + <version>1.0.13</version> + </dependency> + <dependency> + <groupId>org.apache.xmlgraphics</groupId> + <artifactId>batik-dom</artifactId> + <version>1.7</version> + </dependency> + <dependency> + <groupId>org.apache.xmlgraphics</groupId> + <artifactId>batik-svggen</artifactId> + <version>1.7</version> + </dependency> + <dependency> + <groupId>com.lowagie</groupId> + <artifactId>itext</artifactId> + <version>2.1.7</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-math</artifactId> + <version>2.2</version> + </dependency> + </dependencies> + <repositories> + <repository> + <id>jboss-repo2</id> + <name>JBoss repo2</name> + <url>http://repository.jboss.org/maven2</url> + </repository> + </repositories> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/App.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,13 @@ +package de.intevation.flys.artifacts; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,926 @@ +package de.intevation.flys.artifacts; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import javax.xml.xpath.XPathConstants; + +import gnu.trove.TDoubleArrayList; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.DefaultArtifact; +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.data.StateData; +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; +import de.intevation.artifactdatabase.transition.TransitionEngine; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.Range; +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.model.DischargeTables; +import de.intevation.flys.artifacts.model.RiverFactory; +import de.intevation.flys.artifacts.states.DefaultState; + + +/** + * The defaul FLYS artifact. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class FLYSArtifact extends DefaultArtifact { + + /** The logger that is used in this artifact.*/ + private static Logger logger = Logger.getLogger(FLYSArtifact.class); + + + /** The XPath that points to the input data elements of the FEED document.*/ + public static final String XPATH_FEED_INPUT = + "/art:action/art:data/art:input"; + + /** The XPath that points to the name of the target state of ADVANCE.*/ + public static final String XPATH_ADVANCE_TARGET = + "/art:action/art:target/@art:name"; + + /** The constant string that shows that an operation was successful.*/ + public static final String OPERATION_SUCCESSFUL = "SUCCESS"; + + /** The constant string that shows that an operation failed.*/ + public static final String OPERATION_FAILED = "FAILURE"; + + /** The default number of steps between the start end end of a selected Q + * range.*/ + public static final int DEFAULT_Q_STEPS = 30; + + /** The default step width between the start end end kilometer.*/ + public static final double DEFAULT_KM_STEPS = 0.1; + + + /** The identifier of the current state. */ + protected String currentStateId; + + /** The identifiers of previous states on a stack.*/ + protected Vector<String> previousStateIds; + + /** The name of the artifact.*/ + protected String name; + + /** The data that have been inserted into this artifact.*/ + protected Map<String, StateData> data; + + + /** + * The default constructor that creates an empty FLYSArtifact. + */ + public FLYSArtifact() { + data = new HashMap<String, StateData>(); + previousStateIds = new Vector<String>(); + } + + + /** + * Returns the name of the concrete artifact. + * + * @return the name of the concrete artifact. + */ + public abstract String getName(); + + + /** + * Returns the FLYSContext from context object. + * + * @param context The CallContext or the FLYSContext. + * + * @return the FLYSContext. + */ + protected FLYSContext getFlysContext(Object context) { + return context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) ((CallContext) context).globalContext(); + } + + + /** + * Initialize the artifact and insert new data if <code>data</code> contains + * information necessary for this artifact. + * + * @param identifier The UUID. + * @param factory The factory that is used to create this artifact. + * @param context The CallContext. + * @param data Some optional data. + */ + @Override + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + Document data) + { + logger.debug("Setup this artifact with the uuid: " + identifier); + + super.setup(identifier, factory, context, data); + + FLYSContext flysContext = (FLYSContext) context; + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + String name = getName(); + + logger.debug("Set initial state for artifact '" + name + "'"); + List<State> states = engine.getStates(name); + + setCurrentState(states.get(0)); + } + + + /** + * Insert new data included in <code>input</code> into the current state. + * + * @param target XML document that contains new data. + * @param context The CallContext. + * + * @return a document that contains a SUCCESS or FAILURE message. + */ + @Override + public Document feed(Document target, CallContext context) { + logger.info("FLYSArtifact.feed()"); + + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element result = creator.create("result"); + doc.appendChild(result); + + try { + saveData(target, XPATH_FEED_INPUT, context); + return describe(target, context); + } + catch (IllegalArgumentException iae) { + creator.addAttr(result, "type", OPERATION_FAILED, true); + + result.setTextContent(iae.getMessage()); + } + + return doc; + } + + + /** + * This method handles request for changing the current state of an + * artifact. It is possible to step forward or backward. + * + * @param target The incoming ADVANCE document. + * @param context The CallContext. + * + * @return a document that contains a SUCCESS or FAILURE message. + */ + public Document advance(Document target, CallContext context) { + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element result = ec.create("result"); + + String targetState = XMLUtils.xpathString( + target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE); + + logger.info("FLYSArtifact.advance() to '" + targetState + "'"); + + if (isStateReachable(targetState, context)) { + logger.info("Advance: Step forward"); + + Vector<String> prev = getPreviousStateIds(); + prev.add(getCurrentStateId()); + + setCurrentStateId(targetState); + + return describe(target, context); + } + else if (isPreviousState(targetState, context)) { + logger.info("Advance: Step back to"); + + Vector<String> prevs = getPreviousStateIds(); + int targetIdx = prevs.indexOf(targetState); + int start = prevs.size() - 1; + + for (int i = start; i >= targetIdx; i--) { + String prev = prevs.get(i); + logger.debug("Remove state id '" + prev + "'"); + prevs.remove(prev); + } + + setCurrentStateId(targetState); + + return describe(target, context); + } + + logger.warn("Advance: Cannot advance to '" + targetState + "'"); + ec.addAttr(result, "type", OPERATION_FAILED, true); + + doc.appendChild(result); + + return doc; + } + + + /** + * Returns the identifier of the current state. + * + * @return the identifier of the current state. + */ + protected String getCurrentStateId() { + return currentStateId; + } + + + /** + * Sets the identifier of the current state. + * + * @param id the identifier of a state. + */ + protected void setCurrentStateId(String id) { + currentStateId = id; + } + + + /** + * Set the current state of this artifact. <b>NOTE</b>We don't store the + * State object itself - which is not necessary - but its identifier. So + * this method will just call the setCurrentStateId() method with the + * identifier of <i>state</i>. + * + * @param state The new current state. + */ + protected void setCurrentState(State state) { + setCurrentStateId(state.getID()); + } + + + /** + * Returns the current state of the artifact. + * + * @return the current State of the artifact. + */ + protected State getCurrentState(Object context) { + FLYSContext flysContext = getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + return engine.getState(getCurrentStateId()); + } + + + /** + * Returns the vector of previous state identifiers. + * + * @return the vector of previous state identifiers. + */ + protected Vector<String> getPreviousStateIds() { + return previousStateIds; + } + + + /** + * Adds a new StateData item to the data pool of this artifact. + * + * @param name the name of the data object. + * @param data the data object itself. + */ + protected void addData(String name, StateData data) { + this.data.put(name, data); + } + + + /** + * This method returns a specific StateData object that is stored in the + * data pool of this artifact. + * + * @param name The name of the data object. + * + * @return the StateData object if existing, otherwise null. + */ + public StateData getData(String name) { + return data.get(name); + } + + + /** + * This method stores the data that is contained in the FEED document. + * + * @param feed The FEED document. + * @param xpath The XPath that points to the data nodes. + */ + public void saveData(Document feed, String xpath, CallContext context) + throws IllegalArgumentException + { + if (feed == null || xpath == null || xpath.length() == 0) { + throw new IllegalArgumentException("error_feed_no_data"); + } + + NodeList nodes = (NodeList) XMLUtils.xpath( + feed, + xpath, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + if (nodes == null || nodes.getLength() == 0) { + throw new IllegalArgumentException("error_feed_no_data"); + } + + int count = nodes.getLength(); + logger.debug("Try to save " + count + " data items."); + + for (int i = 0; i < count; i++) { + Node node = nodes.item(i); + + String name = XMLUtils.xpathString( + node, "@art:name", ArtifactNamespaceContext.INSTANCE); + String value = XMLUtils.xpathString( + node, "@art:value", ArtifactNamespaceContext.INSTANCE); + + if (name != null && value != null) { + logger.debug("Save data item for '" + name + "' : " + value); + + addData(name, new DefaultStateData(name, null, null, value)); + } + } + + State current = getCurrentState(context); + DefaultState toValidate = (DefaultState) fillState(current); + + toValidate.validate(this, context); + } + + + /** + * This method fills a state object with the data that have been inserted to + * this artifact. This is necessary to use the isStateReachable() method, + * because the Transitions need to know about the inserted data. + * + * @param state The state that needs to be filled with data. + * + * @return the filled state. + */ + protected State fillState(State state) { + Map<String, StateData> stateData = state.getData(); + + if (stateData == null) { + return state; + } + + Set<String> keys = stateData.keySet(); + + for (String key: keys) { + StateData tmp = getData(key); + + if (tmp != null) { + StateData data = stateData.get(key); + data.setValue(tmp.getValue()); + } + } + + return state; + } + + + /** + * Determines if the state with the identifier <i>stateId</i> is reachable + * from the current state. The determination itself takes place in the + * TransitionEngine. + * + * @param stateId The identifier of a state. + * @param context The context object. + * + * @return true, if the state specified by <i>stateId</i> is reacahble, + * otherwise false. + */ + protected boolean isStateReachable(String stateId, Object context) { + logger.debug("Determine if the state '" + stateId + "' is reachable."); + + FLYSContext flysContext = getFlysContext(context); + + State currentState = fillState(getCurrentState(context)); + StateEngine sEngine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + TransitionEngine tEngine = (TransitionEngine) flysContext.get( + FLYSContext.TRANSITION_ENGINE_KEY); + + return tEngine.isStateReachable(this, stateId, currentState, sEngine); + } + + + /** + * Determines if the state with the identifier <i>stateId</i> is a previous + * state of the current state. + * + * @param stateId The target state identifier. + * @param context The context object. + */ + protected boolean isPreviousState(String stateId, Object context) { + logger.debug("Determine if the state '" + stateId + "' is old."); + + Vector<String> prevs = getPreviousStateIds(); + if (prevs.contains(stateId)) { + return true; + } + + return false; + } + + + /** + * Returns the selected River object based on the 'river' data that might + * have been inserted by the user. + * + * @return the selected River or null if no river has been chosen yet. + */ + public River getRiver() { + StateData dRiver = getData("river"); + + return dRiver != null + ? RiverFactory.getRiver((String) dRiver.getValue()) + : null; + } + + + /** + * Returns the selected distance of points. + * + * @return the selected distance or points. + */ + public double[] getDistance() { + StateData dFrom = getData("ld_from"); + StateData dTo = getData("ld_to"); + StateData dLocations = getData("ld_locations"); + + if (dFrom != null && dTo != null) { + return getDistanceByRange(dFrom, dTo); + } + else if (dLocations != null) { + double[] locations = getLocations(); + return new double[] { locations[0], locations[locations.length-1] }; + } + + logger.warn("No data found for distance determination!"); + + return null; + } + + + /** + * This method returns the given distance + * + * @return an array with lower and upper kilometer range for each + * intersected gauge. + */ + public double[][] getSplittedDistance() { + double[] dist = getDistance(); + List<Gauge> gauges = getGauges(); + + int num = gauges != null ? gauges.size() : 0; + + double[][] res = new double[num][2]; + + for (int i = 0; i < num; i++) { + Range range = gauges.get(i).getRange(); + + double lower = range.getA().doubleValue(); + double upper = range.getB().doubleValue(); + + res[i][0] = dist[0] < lower ? lower : dist[0]; + res[i][1] = dist[1] > upper ? upper : dist[1]; + } + + return res; + } + + + /** + * Returns the selected locations based on a given array of locations. + * + * @param locations The StateData that contains the locations. + * + * @return the selected locations. + */ + public double[] getLocations() { + StateData dLocations = getData("ld_locations"); + String locationStr = dLocations != null + ? (String) dLocations.getValue() + : ""; + + if (locationStr == null || locationStr.length() == 0) { + logger.warn("No valid location string found!"); + return null; + } + + String[] tmp = locationStr.split(" "); + TDoubleArrayList locations = new TDoubleArrayList(); + + for (String l: tmp) { + try { + locations.add(Double.parseDouble(l)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + locations.sort(); + + return locations.toNativeArray(); + } + + + /** + * Returns the selected distance based on a given range (from, to). + * + * @param dFrom The StateData that contains the lower value. + * @param dTo The StateData that contains the upper value. + * + * @return the selected distance. + */ + protected double[] getDistanceByRange(StateData dFrom, StateData dTo) { + double from = Double.parseDouble((String) dFrom.getValue()); + double to = Double.parseDouble((String) dTo.getValue()); + + return new double[] { from, to }; + } + + + /** + * Returns the selected Kms. + * + * @param distance An 2dim array with [lower, upper] values. + * + * @return the selected Kms. + */ + public double[] getKms(double[] distance) { + StateData dStep = getData("ld_step"); + + if (dStep == null) { + logger.warn("No step width given. Cannot compute Kms."); + return null; + } + + double step = Double.parseDouble((String) dStep.getValue()); + + // transform step from 'm' into 'km' + step = step / 1000; + + if (step == 0d) { + step = DEFAULT_KM_STEPS; + } + + return getExplodedValues(distance[0], distance[1], step); + } + + + /** + * Returns the selected Kms. + * + * @return the selected kms. + */ + public double[] getKms() { + double[] distance = getDistance(); + return getKms(distance); + } + + + /** + * Returns the gauge based on the current distance and river. + * + * @return the gauge. + */ + public Gauge getGauge() { + River river = getRiver(); + double[] dist = getDistance(); + + if (logger.isDebugEnabled()) { + logger.debug("Determine gauge for:"); + logger.debug("... river: " + river.getName()); + logger.debug("... distance: " + dist[0] + " - " + dist[1]); + } + + Gauge gauge = river.determineGauge(dist[0], dist[1]); + + String name = gauge != null ? gauge.getName() : "'n/a"; + logger.debug("Found gauge: " + name); + + return gauge; + } + + + /** + * Returns the gauges that match the selected kilometer range. + * + * @return the gauges based on the selected kilometer range. + */ + public List<Gauge> getGauges() { + River river = getRiver(); + double[] dist = getDistance(); + + return river.determineGauges(dist[0], dist[1]); + } + + + /** + * This method returns the Q values. + * + * @return the selected Q values or null, if no Q values are selected. + */ + public double[] getQs() { + StateData dMode = getData("wq_mode"); + StateData dSingle = getData("wq_single"); + + String mode = dMode != null ? (String) dMode.getValue() : ""; + + if (mode.equals("Q")) { + if (dSingle != null) { + return getSingleWQValues(); + } + else { + return getWQTriple(); + } + } + else { + logger.warn("You try to get Qs, but W has been inserted."); + return null; + } + } + + + /** + * Returns the Q values based on a specified kilometer range. + * + * @param range A 2dim array with lower and upper kilometer range. + * + * @return an array of Q values. + */ + public double[] getQs(double[] range) { + StateData dMode = getData("wq_mode"); + StateData dValues = getData("wq_values"); + + String mode = dMode != null ? (String) dMode.getValue() : ""; + + if (mode.equals("Q")) { + return getWQForDist(range); + } + + logger.warn("You try to get Qs, but Ws has been inserted."); + return null; + } + + + /** + * Returns the W values based on a specified kilometer range. + * + * @param range A 2dim array with lower and upper kilometer range. + * + * @return an array of W values. + */ + public double[] getWs(double[] range) { + StateData dMode = getData("wq_mode"); + StateData dValues = getData("wq_values"); + + String mode = dMode != null ? (String) dMode.getValue() : ""; + + if (mode.equals("W")) { + return getWQForDist(range); + } + + logger.warn("You try to get Ws, but Qs has been inserted."); + return null; + } + + + /** + * This method returns the W values. + * + * @return the selected W values or null, if no W values are selected. + */ + public double[] getWs() { + StateData dMode = getData("wq_mode"); + StateData dSingle = getData("wq_single"); + + String mode = dMode != null ? (String) dMode.getValue() : ""; + + if (mode.equals("W")) { + if (dSingle != null) { + return getSingleWQValues(); + } + else { + return getWQTriple(); + } + } + else { + logger.warn("You try to get Qs, but W has been inserted."); + return null; + } + } + + + /** + * Returns the Qs for a number of Ws. This method makes use of + * DischargeTables.getQForW(). + * + * @param ws An array of W values. + * + * @return an array of Q values. + */ + public double[] getQsForWs(double[] ws) { + logger.debug("FLYSArtifact.getQsForWs"); + + River r = getRiver(); + Gauge g = getGauge(); + + DischargeTables dt = new DischargeTables(r.getName(), g.getName()); + Map<String, double [][]> tmp = dt.getValues(); + + double[][] values = tmp.get(g.getName()); + double[] qs = new double[ws.length]; + + for (int i = 0; i < ws.length; i++) { + qs[i] = dt.getQForW(values, ws[i]); + } + + return qs; + } + + + /** + * This method returns the given W or Q values for a specific range + * (inserted in the WQ input panel for discharge longitudinal sections). + * + * @param dist A 2dim array with lower und upper kilometer values. + * + * @return an array of W or Q values. + */ + protected double[] getWQForDist(double[] dist) { + logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]); + StateData data = getData("wq_values"); + + if (data == null) { + logger.warn("Missing wq values!"); + return null; + } + + String dataString = (String) data.getValue(); + String[] ranges = dataString.split(":"); + + for (String range: ranges) { + String[] parts = range.split(";"); + + double lower = Double.parseDouble(parts[0]); + double upper = Double.parseDouble(parts[1]); + + if (lower <= dist[0] && upper >= dist[1]) { + String[] values = parts[2].split(","); + + int num = values.length; + double[] res = new double[num]; + + for (int i = 0; i < num; i++) { + try { + res[i] = Double.parseDouble(values[i]); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + return res; + } + } + + logger.warn("Specified range for WQ not found!"); + + return null; + } + + + /** + * This method returns an array of inserted WQ triples that consist of from, + * to and the step width. + * + * @return an array of from, to and step width. + */ + protected double[] getWQTriple() { + StateData dFrom = getData("wq_from"); + StateData dTo = getData("wq_to"); + + if (dFrom == null || dTo == null) { + logger.warn("Missing start or end value for range."); + return null; + } + + double from = Double.parseDouble((String) dFrom.getValue()); + double to = Double.parseDouble((String) dTo.getValue()); + + StateData dStep = getData("wq_step"); + + if (dStep == null) { + logger.warn("No step width given. Cannot compute Qs."); + return null; + } + + double step = Double.parseDouble((String) dStep.getValue()); + + // if no width is given, the DEFAULT_Q_STEPS is used to compute the step + // width. Maybe, we should round the value to a number of digits. + if (step == 0d) { + double diff = to - from; + step = diff / DEFAULT_Q_STEPS; + } + + return getExplodedValues(from, to, step); + } + + + /** + * Returns an array of inserted WQ double values stored as whitespace + * separated list. + * + * @return an array of W or Q values. + */ + protected double[] getSingleWQValues() { + StateData dSingle = getData("wq_single"); + + if (dSingle == null) { + logger.warn("Cannot determine single WQ values. No data given."); + return null; + } + + String tmp = (String) dSingle.getValue(); + String[] strValues = tmp.split(" "); + + TDoubleArrayList values = new TDoubleArrayList(); + + for (String strValue: strValues) { + try { + values.add(Double.parseDouble(strValue)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + values.sort(); + + return values.toNativeArray(); + } + + + /** + * Returns an array of double values. The values contained in this array + * begin with the value <i>from</i> and end with the value <i>to</i>. The + * number of values in the result array depends on the <i>step</i> width. + * + * @param from The lower value. + * @param to The upper value. + * @param step The step width between two values in the result array. + * + * @return an array of double values. + */ + public double[] getExplodedValues(double from, double to, double step) { + double lower = from; + + double diff = to - from; + double tmp = diff / step; + int num = (int) Math.ceil(tmp) ; + + double[] values = new double[num]; + + for (int idx = 0; idx < num; idx++) { + values[idx] = lower; + lower += step; + } + + return values; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,537 @@ +package de.intevation.flys.artifacts; + +import java.util.List; +import java.util.Vector; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.state.Output; +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; +import de.intevation.artifactdatabase.transition.TransitionEngine; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.states.DefaultState; +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.math.BackJumpCorrector; +import de.intevation.flys.artifacts.model.MainValuesFactory; +import de.intevation.flys.artifacts.model.WQCKms; +import de.intevation.flys.artifacts.model.WQDay; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.WstValueTable; + + +/** + * The default WINFO artifact. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WINFOArtifact extends FLYSArtifact { + + /** The logger for this class */ + private static Logger logger = Logger.getLogger(WINFOArtifact.class); + + + /** The name of the artifact.*/ + public static final String ARTIFACT_NAME = "winfo"; + + /** XPath */ + public static final String XPATH_STATIC_UI ="/art:result/art:ui/art:static"; + + + /** + * The default constructor. + */ + public WINFOArtifact() { + } + + + /** + * This method returns a description of this artifact. + * + * @param data Some data. + * @param context The CallContext. + * + * @return the description of this artifact. + */ + public Document describe(Document data, CallContext context) { + logger.debug("Describe: the current state is: " + getCurrentStateId()); + + FLYSContext flysContext = null; + if (context instanceof FLYSContext) { + flysContext = (FLYSContext) context; + } + else { + flysContext = (FLYSContext) context.globalContext(); + } + + StateEngine stateEngine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + TransitionEngine transitionEngine = (TransitionEngine) flysContext.get( + FLYSContext.TRANSITION_ENGINE_KEY); + + List<State> reachable = transitionEngine.getReachableStates( + this, getCurrentState(context), stateEngine); + + Document description = XMLUtils.newDocument(); + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + description, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ProtocolUtils.createRootNode(creator); + description.appendChild(root); + + State current = getCurrentState(context); + + ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash()); + ProtocolUtils.appendState(creator, root, current); + ProtocolUtils.appendReachableStates(creator, root, reachable); + + Element name = ProtocolUtils.createArtNode( + creator, "name", + new String[] { "value" }, + new String[] { getName() }); + + Element ui = ProtocolUtils.createArtNode( + creator, "ui", null, null); + + Element staticUI = ProtocolUtils.createArtNode( + creator, "static", null, null); + + Element outs = ProtocolUtils.createArtNode( + creator, "outputmodes", null, null); + appendOutputModes(description, outs, context, identifier()); + + appendStaticUI(description, staticUI, context, identifier()); + + Element dynamic = current.describe( + this, + description, + root, + context, + identifier()); + + if (dynamic != null) { + ui.appendChild(dynamic); + } + + ui.appendChild(staticUI); + + root.appendChild(name); + root.appendChild(ui); + root.appendChild(outs); + + return description; + } + + + /** + * Returns the name of the concrete artifact. + * + * @return the name of the concrete artifact. + */ + public String getName() { + return ARTIFACT_NAME; + } + + + protected void appendOutputModes( + Document doc, + Element outs, + CallContext context, + String uuid) + { + Vector<String> stateIds = getPreviousStateIds(); + + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + FLYSContext flysContext = getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + for (String stateId: stateIds) { + logger.debug("Append output modes for state: " + stateId); + State state = engine.getState(stateId); + + List<Output> list = state.getOutputs(); + if (list == null || list.size() == 0) { + continue; + } + + ProtocolUtils.appendOutputModes(creator, outs, list); + } + + try { + DefaultState cur = (DefaultState) getCurrentState(context); + if (cur.validate(this, context)) { + List<Output> list = cur.getOutputs(); + if (list != null && list.size() > 0) { + logger.debug( + "Append output modes for state: " + cur.getID()); + + ProtocolUtils.appendOutputModes(creator, outs, list); + } + } + } + catch (IllegalArgumentException iae) { + // state is not valid, so we do not append its outputs. + } + } + + + /** + * This method appends the static data - that has already been inserted by + * the user - to the static node of the DESCRIBE document. + * + * @param doc The document. + * @param ui The root node. + * @param context The CallContext. + * @param uuid The identifier of the artifact. + */ + protected void appendStaticUI( + Document doc, + Node ui, + CallContext context, + String uuid) + { + Vector<String> stateIds = getPreviousStateIds(); + + FLYSContext flysContext = getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + for (String stateId: stateIds) { + logger.debug("Append static data for state: " + stateId); + DefaultState state = (DefaultState) engine.getState(stateId); + state = (DefaultState) fillState(state); + + ui.appendChild(state.describeStatic(doc, ui, context, uuid)); + } + } + + + // + // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES + // + + /** + * Returns the data that is computed by a waterlevel computation. + * + * @return an array of data triples that consist of W, Q and Kms. + */ + public WQKms[] getWaterlevelData() + throws NullPointerException + { + logger.debug("WINFOArtifact.getWaterlevelData"); + + River river = getRiver(); + if (river == null) { + throw new NullPointerException("No river selected."); + } + + double[] kms = getKms(); + if (kms == null) { + throw new NullPointerException("No Kms selected."); + } + + double[] qs = getQs(); + if (qs == null) { + logger.debug("Determine Q values based on a set of W values."); + + double[] ws = getWs(); + qs = getQsForWs(ws); + } + + WstValueTable wst = WstValueTable.getTable(river); + if (wst == null) { + throw new NullPointerException("No Wst found for selected river."); + } + + // TODO Introduce a caching mechanism here! + + return computeWaterlevelData(kms, qs, wst); + } + + + /** + * Computes the data of a waterlevel computation based on the interpolation + * in WstValueTable. + * + * @param kms The kilometer values. + * @param qa The discharge values. + * @param wst The WstValueTable used for the interpolation. + * + * @return an array of data triples that consist of W, Q and Kms. + */ + public static WQKms[] computeWaterlevelData( + double[] kms, + double[] qs, + WstValueTable wst) + { + logger.info("WINFOArtifact.computeWaterlevelData"); + + WQKms[] wqkms = new WQKms[qs.length]; + for (int i = 0; i < wqkms.length; i++) { + wqkms[i] = new WQKms(kms.length); + } + + double [] interpolatedW = new double[qs.length]; + + for (double km: kms) { + wst.interpolateW(km, qs, interpolatedW); + + // TODO Modify the interpolation to return a better formed data + // structure. + for (int i = 0; i < interpolatedW.length; i++) { + wqkms[i].add(interpolatedW[i], qs[i], km); + } + } + + return wqkms; + } + + + /** + * Returns the data that is computed by a duration curve computation. + * + * @return the data computed by a duration curve computation. + */ + public WQDay getDurationCurveData() + throws NullPointerException + { + logger.debug("WINFOArtifact.getDurationCurveData"); + + River r = getRiver(); + + if (r == null) { + throw new NullPointerException("Cannot determine river."); + } + + Gauge g = getGauge(); + + if (g == null) { + throw new NullPointerException("Cannot determine gauge."); + } + + double[] locations = getLocations(); + + if (locations == null) { + throw new NullPointerException("Cannot determine location."); + } + + WstValueTable wst = WstValueTable.getTable(r); + if (wst == null) { + throw new NullPointerException("No Wst found for selected river."); + } + + // TODO Introduce a caching mechanism here! + + return computeDurationCurveData(g, wst, locations[0]); + } + + + /** + * Computes the data used to create duration curves. + * + * @param gauge The selected gauge. + * @param location The selected location. + * + * @return the computed data. + */ + public static WQDay computeDurationCurveData( + Gauge gauge, + WstValueTable wst, + double location) + { + logger.info("WINFOArtifact.computeDurationCurveData"); + + Object[] obj = MainValuesFactory.getDurationCurveData(gauge); + + int[] days = (int[]) obj[0]; + double[] qs = (double[]) obj[1]; + + double[] interpolatedW = new double[qs.length]; + interpolatedW = wst.interpolateW(location, qs, interpolatedW); + + WQDay wqday = new WQDay(qs.length); + + for (int i = 0; i < days.length; i++) { + wqday.add(days[i], interpolatedW[i], qs[i]); + } + + return wqday; + } + + + /** + * Returns the data that is computed by a discharge curve computation. + * + * @return the data computed by a discharge curve computation. + */ + public WQKms getComputedDischargeCurveData() + throws NullPointerException + { + logger.debug("WINFOArtifact.getComputedDischargeCurveData"); + + River r = getRiver(); + + if (r == null) { + throw new NullPointerException("Cannot determine river."); + } + + double[] locations = getLocations(); + + if (locations == null) { + throw new NullPointerException("Cannot determine location."); + } + + WstValueTable wst = WstValueTable.getTable(r); + if (wst == null) { + throw new NullPointerException("No Wst found for selected river."); + } + + // TODO Introduce a caching mechanism here! + + return computeDischargeCurveData(wst, locations[0]); + } + + + /** + * Computes the data used to create computed discharge curves. + * + * @param wst The WstValueTable that is used for the interpolation. + * @param location The location where the computation should be based on. + * + * @return an object that contains tuples of W/Q values at the specified + * location. + */ + public static WQKms computeDischargeCurveData( + WstValueTable wst, + double location) + { + logger.info("WINFOArtifact.computeDischargeCurveData"); + + double[][] wqs = wst.interpolateWQ(location); + + if (wqs == null) { + logger.error("Cannot compute discharge curve data."); + return null; + } + + double[] ws = wqs[0]; + double[] qs = wqs[1]; + + WQKms wqkms = new WQKms(ws.length); + + for (int i = 0; i < ws.length; i++) { + wqkms.add(ws[i], qs[i], location); + } + + return wqkms; + } + + + /** + * Returns the data computed by the discharge longitudinal section + * computation. + * + * @return an array of WQKms object - one object for each given Q value. + */ + public WQKms[] getDischargeLongitudinalSectionData() { + logger.debug("WINFOArtifact.getDischargeLongitudinalSectionData"); + + River river = getRiver(); + if (river == null) { + logger.error("No river selected."); + } + + WstValueTable wst = WstValueTable.getTable(river); + if (wst == null) { + logger.error("No Wst found for selected river."); + } + + double[][] dist = getSplittedDistance(); + int num = dist != null ? dist.length : 0; + + WQKms[][] wqkms = new WQKms[num][]; + + for (int i = 0; i < num; i++) { + double[] kms = getKms(dist[i]); + if (kms == null) { + // XXX maybe we should cancel this operation here. + continue; + } + + double[] qs = getQs(dist[i]); + if (qs == null) { + logger.debug("Determine Q values based on a set of W values."); + + double[] ws = getWs(dist[i]); + qs = getQsForWs(ws); + } + + wqkms[i] = computeWaterlevelData(kms, qs, wst); + } + + WQKms[] merged = WQKms.merge(wqkms); + int numMerged = merged.length; + WQKms[] computed = new WQKms[numMerged]; + + for (int i = 0; i < numMerged; i++) { + computed[i] = computeDischargeLongitudinalSectionData(merged[i]); + } + + // TODO Introduce a caching mechanism here! + + return computed; + } + + + /** + * Computes the data used for a discharge longitudinal section based on a + * given WQKms object. If there are backjumps while computing the data, a + * WQCKms object is returned, otherwise the incoming wqkms object. + * + * @param wqkms The WQKms object that contains W, Q and Kms. + * + * @return an instance of WQKms or WQCKms. + */ + public static WQKms computeDischargeLongitudinalSectionData(WQKms wqkms) { + logger.info("WINFOArtifact.computeDischargeLongitudinalSectionData"); + + BackJumpCorrector bjc = new BackJumpCorrector(); + + bjc.doCorrection(wqkms.getKms(), wqkms.getWs()); + + if (bjc.hasBackJumps()) { + logger.info("Discharge longitudinal section has backjumps."); + return new WQCKms( + wqkms.getKms(), + wqkms.getQs(), + wqkms.getWs(), + bjc.getCorrected()); + } + else { + logger.info("Discharge longitudinal section has no backjumps."); + return wqkms; + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WaterlevelState.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,29 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +public class WaterlevelState extends DefaultState { + + /** The logger that is used in this state.*/ + private static Logger logger = Logger.getLogger(WaterlevelState.class); + + + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("WaterlevelState.createItems"); + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,61 @@ +package de.intevation.flys.artifacts.cache; + +import de.intevation.artifacts.common.utils.Config; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheException; +import net.sf.ehcache.CacheManager; + +import org.apache.log4j.Logger; + +public final class CacheFactory +{ + private static Logger log = Logger.getLogger(CacheFactory.class); + + public static final String XPATH_CACHE_CONFIG_FILE = + "/artifact-database/cache/config-file/text()"; + + private CacheFactory() { + } + + private static boolean initialized; + + private static CacheManager cacheManager; + + public static final Cache getCache() { + return getCache(Cache.DEFAULT_CACHE_NAME); + } + + public static final synchronized Cache getCache(String cacheName) { + if (!initialized) { + initialized = true; // try only once + String configFile = Config.getStringXPath(XPATH_CACHE_CONFIG_FILE); + if (configFile != null) { + configFile = Config.replaceConfigDir(configFile); + try { + cacheManager = CacheManager.create(configFile); + //System.setProperty( + // "net.sf.ehcache.enableShutdownHook", "true"); + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + log.info("shutting down caches"); + for (String name: cacheManager.getCacheNames()) { + log.info("\tflushing '" + name + "'"); + cacheManager.getCache(name).flush(); + } + cacheManager.shutdown(); + } + }); + } + catch (CacheException ce) { + log.error("cannot configure cache", ce); + } + } + } + + return cacheManager != null + ? cacheManager.getCache(cacheName) + : null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,57 @@ +package de.intevation.flys.artifacts.context; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import de.intevation.artifactdatabase.DefaultArtifactContext; + + +/** + * This class is used to store application wide information in a global context. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class FLYSContext extends DefaultArtifactContext { + + /** The logger used in this class */ + private static Logger logger = Logger.getLogger(FLYSContext.class); + + /** The key that is used to store the TransitionEngine in the context */ + public static final String TRANSITION_ENGINE_KEY = + "artifact.transition.engine"; + + /** The key that is used to store the StateEngine in the context */ + public static final String STATE_ENGINE_KEY = + "artifact.state.engine"; + + /** The key that is used to store the Map of OutGenerator classes in the + * context.*/ + public static final String OUTGENERATORS_KEY = + "flys.export.outgenerators"; + + /** The key that is used to store the map of themes in the context.*/ + public static final String THEMES = + "flys.themes.map"; + + /** The key that is used to store a map of theme mappings in the context.*/ + public static final String THEME_MAPPING = + "flys.themes.mapping.map"; + + + /** + * The default constructor. + */ + public FLYSContext() { + super(); + } + + + /** + * A constructor with a config document. + */ + public FLYSContext(Document config) { + super(config); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,363 @@ +package de.intevation.flys.artifacts.context; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.ArtifactContextFactory; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; +import de.intevation.artifactdatabase.transition.Transition; +import de.intevation.artifactdatabase.transition.TransitionEngine; + +import de.intevation.flys.artifacts.states.StateFactory; +import de.intevation.flys.artifacts.transitions.TransitionFactory; +import de.intevation.flys.themes.Theme; +import de.intevation.flys.themes.ThemeFactory; + + +/** + * The ArtifactContextFactory is used to initialize basic components and put + * them into the global context of the application. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class FLYSContextFactory implements ArtifactContextFactory { + + /** The logger that is used in this class */ + private static Logger logger = Logger.getLogger(FLYSContextFactory.class); + + /** The XPath to the artifacts configured in the configuration. */ + public static final String XPATH_ARTIFACTS = + "/artifact-database/artifacts/artifact"; + + /** The XPath to the name of the artifact. */ + public static final String XPATH_ARTIFACT_NAME = "/artifact/@name"; + + /** The XPath to the xlink ref in an artifact configuration. */ + public static final String XPATH_XLINK = "xlink:href"; + + /** The XPath to the transitions configured in the artifact config. */ + public static final String XPATH_TRANSITIONS = + "/artifact/states/transition"; + + /** The XPath to the states configured in the artifact config. */ + public static final String XPATH_STATES = + "/artifact/states/state"; + + public static final String XPATH_OUTPUT_GENERATORS = + "/artifact-database/output-generators/output-generator"; + + public static final String XPATH_THEME_CONFIG = + "/artifact-database/flys/themes/configuration/text()"; + + public static final String XPATH_THEMES = + "/themes/theme"; + + public static final String XPATH_THEME_MAPPINGS = + "/themes/mappings/mapping"; + + /** + * Creates a new FLYSArtifactContext object and initialize all + * components required by the application. + * + * @param config The artifact server configuration. + * @return a FLYSArtifactContext. + */ + public Object createArtifactContext(Document config) { + FLYSContext context = new FLYSContext(config); + + configureTransitions(config, context); + configureStates(config, context); + configureOutGenerators(config, context); + configureThemes(config, context); + configureThemesMappings(config, context); + + return context; + } + + + /** + * This method initializes the transition configuration. + * + * @param config the config document. + * @param context the FLYSContext. + */ + protected void configureTransitions(Document config, FLYSContext context) { + TransitionEngine engine = new TransitionEngine(); + + Document[] artifacts = getArtifactConfigurations(config); + logger.info("Found " + artifacts.length + " artifacts in the config."); + + for (Document doc: artifacts) { + List<Transition> transitions = new ArrayList<Transition>(); + + String artName = (String) XMLUtils.xpath( + doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING); + + NodeList list = (NodeList) XMLUtils.xpath( + doc, XPATH_TRANSITIONS, XPathConstants.NODESET); + + if (list == null) { + logger.warn("The artifact has no transitions configured."); + continue; + } + + int trans = list.getLength(); + + logger.info( + "Artifact '" + artName + "' has " + trans + " transitions."); + + for (int i = 0; i < trans; i++) { + Transition t = TransitionFactory.createTransition(list.item(i)); + String s = t.getFrom(); + engine.addTransition(s, t); + } + } + + context.put(FLYSContext.TRANSITION_ENGINE_KEY, engine); + } + + + /** + * This method returns all artifact documents defined in + * <code>config</code>. <br>NOTE: The artifact configurations need to be + * stored in own files referenced by an xlink. + * + * @param config The global configuration. + * + * @return an array of Artifact configurations. + */ + protected Document[] getArtifactConfigurations(Document config) { + NodeList artifacts = (NodeList) XMLUtils.xpath( + config, XPATH_ARTIFACTS, XPathConstants.NODESET); + + int count = artifacts.getLength(); + + Document[] artifactDocs = new Document[count]; + + for (int i = 0; i < count; i++) { + Element tmp = (Element) artifacts.item(i); + + String xlink = tmp.getAttribute(XPATH_XLINK); + xlink = Config.replaceConfigDir(xlink); + + File artifactFile = new File(xlink); + artifactDocs[i] = XMLUtils.parseDocument(artifactFile); + } + + return artifactDocs; + } + + + /** + * This method initializes the transition configuration. + * + * @param config the config document. + * @param context the FLYSContext. + */ + protected void configureStates(Document config, FLYSContext context) { + StateEngine engine = new StateEngine(); + + Document[] artifacts = getArtifactConfigurations(config); + logger.info("Found " + artifacts.length + " artifacts in the config."); + + for (Document doc: artifacts) { + List<State> states = new ArrayList<State>(); + + String artName = (String) XMLUtils.xpath( + doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING); + + NodeList stateList = (NodeList) XMLUtils.xpath( + doc, XPATH_STATES, XPathConstants.NODESET); + + if (stateList == null) { + logger.warn("The artifact has no states configured."); + continue; + } + + int count = stateList.getLength(); + + logger.info( + "Artifact '" + artName + "' has " + count + " states."); + + for (int i = 0; i < count; i++) { + states.add(StateFactory.createState( + stateList.item(i))); + } + + engine.addStates(artName, states); + } + + context.put(FLYSContext.STATE_ENGINE_KEY, engine); + } + + + /** + * This method intializes the provided output generators. + * + * @param config the config document. + * @param context the FLYSContext. + */ + protected void configureOutGenerators(Document config, FLYSContext context){ + Map<String, Class> generators = new HashMap<String, Class>(); + + NodeList outGenerators = (NodeList) XMLUtils.xpath( + config, + XPATH_OUTPUT_GENERATORS, + XPathConstants.NODESET); + + int num = outGenerators == null ? 0 : outGenerators.getLength(); + + if (num == 0) { + logger.warn("No output generators configured in this application."); + return; + } + + logger.info("Found " + num + " configured output generators."); + + int idx = 0; + + for (int i = 0; i < num; i++) { + Node item = outGenerators.item(i); + + String name = (String) XMLUtils.xpath( + item, "@name", XPathConstants.STRING); + + String clazz = (String) XMLUtils.xpath( + item, "text()", XPathConstants.STRING); + + if (name == null || clazz == null) { + continue; + } + + try { + generators.put(name, Class.forName(clazz)); + + idx++; + } + catch (ClassNotFoundException cnfe) { + logger.warn(cnfe, cnfe); + } + } + + logger.info("Successfully loaded " + idx + " output generators."); + context.put(FLYSContext.OUTGENERATORS_KEY, generators); + } + + + /** + * This methods reads the configured themes and puts them into the + * FLYSContext. + * + * @param config The global configuration. + * @param context The FLYSContext. + */ + protected void configureThemes(Document config, FLYSContext context) { + logger.debug("FLYSContextFactory.configureThemes"); + + Document cfg = getThemeConfig(config); + + NodeList themes = (NodeList) XMLUtils.xpath( + cfg, XPATH_THEMES, XPathConstants.NODESET); + + int num = themes != null ? themes.getLength() : 0; + + if (num == 0) { + logger.warn("There are no themes configured!"); + return; + } + + logger.debug("Found " + num + " configured themes."); + + Map<String, Theme> theThemes = new HashMap<String, Theme>(); + + for (int i = 0; i < num; i++) { + Node theme = themes.item(i); + + Theme theTheme = ThemeFactory.createTheme(cfg, theme); + + if (theme != null) { + theThemes.put(theTheme.getName(), theTheme); + } + } + + logger.debug("Initialized " + theThemes.size() + "/" + num + " themes"); + + context.put(FLYSContext.THEMES, theThemes); + } + + + /** + * This method is used to retrieve the theme configuration document. + * + * @param config The global configuration. + * + * @return the theme configuration. + */ + protected Document getThemeConfig(Document config) { + String themeConfig = (String) XMLUtils.xpath( + config, + XPATH_THEME_CONFIG, + XPathConstants.STRING); + + themeConfig = Config.replaceConfigDir(themeConfig); + + logger.debug("Parse theme cfg: " + themeConfig); + + return XMLUtils.parseDocument(new File(themeConfig)); + } + + + protected void configureThemesMappings(Document cfg, FLYSContext context) { + logger.debug("FLYSContextFactory.configureThemesMappings"); + + Document config = getThemeConfig(cfg); + + NodeList mappings = (NodeList) XMLUtils.xpath( + config, XPATH_THEME_MAPPINGS, XPathConstants.NODESET); + + int num = mappings != null ? mappings.getLength() : 0; + + if (num == 0) { + logger.warn("No theme <--> facet mappins found!"); + return; + } + + Map<String, String> mapping = new HashMap<String, String>(); + + for (int i = 0; i < num; i++) { + Node node = mappings.item(i); + + String from = (String) XMLUtils.xpath( + node, "@from", XPathConstants.STRING); + + String to = (String) XMLUtils.xpath( + node, "@to", XPathConstants.STRING); + + if (from != null && to != null) { + mapping.put(from, to); + } + } + + logger.debug("Found " + mapping.size() + " theme/facet mappings."); + + context.put(FLYSContext.THEME_MAPPING, mapping); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,68 @@ +package de.intevation.flys.artifacts.context; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import org.hibernate.Session; + +import de.intevation.flys.backend.SessionHolder; + +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallContext.Listener; + + +/** + * This CallContextListener is used to initialize a ThreadLocal variable in + * each CallContext (for each request) that holds Sessions. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class SessionCallContextListener implements Listener { + + public static final String SESSION_KEY = "context.session"; + + /** The logger that is used in this class.*/ + private static Logger logger = + Logger.getLogger(SessionCallContextListener.class); + + + public SessionCallContextListener() { + } + + + public void setup(Document config, Node listenerConfig) { + // nothing to do here + } + + + /** + * Initializes a ThreadLocal variable that is used to hold sessions. + * + * @param context The CallContext. + */ + public void init(CallContext context) { + logger.debug("SessionCallContextListener.init"); + + Session session = SessionHolder.acquire(); + + context.putContextValue(SESSION_KEY, session); + } + + + /** + * Closes open sessions of the ThreadLocal variable opened in init(). + * + * @param context The CallContext. + */ + public void close(CallContext context) { + logger.debug("SessionCallContextListener.close"); + + Session session = (Session)context.getContextValue(SESSION_KEY); + session.close(); + + SessionHolder.release(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,228 @@ +package de.intevation.flys.artifacts.math; + +import java.util.ArrayList; +import java.util.List; + +import java.io.Serializable; + +import org.apache.commons.math.analysis.interpolation.SplineInterpolator; + +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; + +import org.apache.commons.math.ArgumentOutsideDomainException; + +import org.apache.log4j.Logger; + +public class BackJumpCorrector +implements Serializable +{ + private static Logger log = Logger.getLogger(BackJumpCorrector.class); + + protected ArrayList<Double> backjumps; + + protected double [] corrected; + + public BackJumpCorrector() { + backjumps = new ArrayList<Double>(); + } + + public boolean hasBackJumps() { + return !backjumps.isEmpty(); + } + + public List<Double> getBackJumps() { + return backjumps; + } + + public double [] getCorrected() { + return corrected; + } + + private static final boolean isIncreasing(double [] ws) { + int inc = 0; + int dec = 0; + int sweet = (ws.length-1)/2; + for (int i = 1; i < ws.length; ++i) { + if (ws[i] > ws[i-1]) { + if (++inc > sweet) { + return true; + } + } + else if (++dec > sweet) { + return false; + } + } + return inc > sweet; + } + + public boolean doCorrection(double [] km, double [] ws) { + boolean isIncreasing = isIncreasing(ws); + + if (isIncreasing) { + // mirror along x axis to simulate decreasing values + ws = (double [])ws.clone(); + for (int i = 0; i < ws.length; ++i) { + ws[i] = -ws[i]; + } + } + + boolean hasBackJumps = doCorrectionClean(km, ws); + + if (hasBackJumps && isIncreasing) { + // mirror back + for (int i = 0; i < corrected.length; ++i) { + corrected[i] = -corrected[i]; + } + } + + return hasBackJumps; + } + + protected boolean doCorrectionClean(double [] km, double [] ws) { + int N = km.length; + + if (N != ws.length) { + throw new IllegalArgumentException("km.length != ws.length"); + } + + if (N < 2) { + return false; + } + + SplineInterpolator interpolator = null; + + for (int i = 1; i < N; ++i) { + if (ws[i] <= ws[i-1]) { + // no back jump + continue; + } + backjumps.add(km[i]); + + if (corrected == null) { + // lazy cloning + ws = corrected = (double [])ws.clone(); + } + + int back = i-2; + double distance = Math.abs(km[i] - km[i-1]); + double rest = 0.0; + double ikm = 0.0; + + while (back > -1) { + if (ws[back] < ws[i]) { // under water + // continue scanning backwards + distance += Math.abs(km[back+1] - km[back]); + --back; + continue; + } + if (ws[back] > ws[i]) { // over water + // need to calculate intersection + log.debug("intersection case"); + double m = (km[back+1]-km[back])/(ws[back+1]-ws[back]); + double b = km[back]-ws[back]*m; + ikm = m*ws[i] + b; + distance += Math.abs(ikm - km[back+1]); + rest = Math.abs(km[back] - ikm); + } + else { + // equals height at ws[back] + log.debug("exact height match"); + distance += Math.abs(km[back+1] - km[back]); + ikm = km[back]; + } + break; + } + + if (back < 0) { + log.debug("run over left border"); + // fill with same as ws[i] + for (int j = 0; j < i; ++j) { + ws[j] = ws[i]; + } + continue; + } + + double quarterDistance = 0.25*distance; + + // Now further seek back for the max height + + double restDistance = Math.max(0.0, quarterDistance - rest); + --back; + + double mkmw = ws[i]; + double mkm = km[0]; + + while (back > -1) { + double d = Math.abs(km[back+1] - km[back]); + restDistance -= d; + if (restDistance > 0.0) { + --back; + continue; + } + if (restDistance < 0.0) { + // need to calculate intersection + if (km[back] == km[back+1]) { // should not happen + mkm = km[back]; + mkmw = 0.5*(ws[back] + ws[back+1]); + } + else { + double m = (ws[back+1]-ws[back])/(km[back+1]-km[back]); + double b = ws[back] - km[back]*m; + mkm = km[back] + restDistance; + mkmw = m*mkm + b; + } + } + else { + // exact match + mkm = km[back]; + mkmw = ws[back]; + } + break; + } + + double factor = back >= 0 && Math.abs(restDistance) < 1e-4 + ? 1.0 + : 1.0 - Math.min(1, Math.max(0, restDistance/quarterDistance)); + + double ikmw = factor*0.25*(mkmw-ws[i]) + ws[i]; + + double end = ikm + quarterDistance*factor; + + double [] x = { mkm, ikm, end }; + double [] y = { mkmw, ikmw, ws[i] }; + + if (interpolator == null) { + interpolator = new SplineInterpolator(); + } + + PolynomialSplineFunction spline = interpolator.interpolate(x, y); + + try { + if (log.isDebugEnabled()) { + log.debug("spline points:"); + for (int j = 0; j < x.length; ++j) { + log.debug(x[j] + " " + y[j] + " " + spline.value(x[j])); + } + } + + for (back = Math.max(back, 0); + back < i && km[back] < end; + ++back + ) { + // to 3/4 point fill with spline values + ws[back] = spline.value(km[back]); + } + while (back < i) { + // fill the rest with ws[i] + ws[back++] = ws[i]; + } + } + catch (ArgumentOutsideDomainException aode) { + log.error("spline interpolation failed", aode); + } + } // for all km + + return !backjumps.isEmpty(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,78 @@ +package de.intevation.flys.artifacts.math; + +public class LinearRemap +{ + public static class Segment { + + protected Segment next; + + protected double from; + protected double to; + + protected double m; + protected double b; + + public Segment() { + } + + public Segment( + double from, double to, + double m, double b, + Segment next + ) { + this.from = from; + this.to = to; + this.m = m; + this.b = b; + } + + public double eval(double x) { + return m*x + b; + } + } // class Segment + + protected Segment head; + + public LinearRemap() { + } + + public void add( + double from, double to, + double x1, double y1, + double x2, double y2 + ) { + // y1 = m*x1 + b <=> b = y1 - m*x1 + // y2 = m*x2 + b + // y2 - y1 = m*(x2 - x1) + // m = (y2 - y1)/(x2 - x1) + + double m, b; + + if (x2 == x1) { + m = 0.0; + b = 0.5*(y2 + y1); + } + else { + m = (y2 - y1)/(x2 - x1); + b = y1 - m*x1; + } + + head = new Segment(from, to, m, b, head); + } + + public double eval(double pos, double x) { + Segment current = head; + + while (current != null) { + + if (pos >= current.from && pos <= current.to) { + return current.eval(x); + } + + current = current.next; + } + + return Double.NaN; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,37 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.Annotation; +import de.intevation.flys.model.Range; +import de.intevation.flys.model.River; + +import org.hibernate.Session; +import org.hibernate.Query; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class AnnotationsFactory { + + public static List<Annotation> getAnnotations(River river) { + return getAnnotations(river.getName()); + } + + + public static List<Annotation> getAnnotations(String river) { + Session session = SessionHolder.HOLDER.get(); + + Query rangesQuery = session.createQuery( + "from Range where river.name=:name"); + rangesQuery.setParameter("name", river); + List<Range> ranges = rangesQuery.list(); + + Query query = session.createQuery( + "from Annotation where range in (:ranges) order by range.a"); + query.setParameterList("ranges", ranges); + return query.list(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,234 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.Arrays; +import java.util.Comparator; + +import java.io.Serializable; + +import org.hibernate.Session; +import org.hibernate.Query; + +import org.apache.log4j.Logger; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.DischargeTable; +import de.intevation.flys.model.DischargeTableValue; + +public class DischargeTables +implements Serializable +{ + private static Logger log = Logger.getLogger(DischargeTables.class); + + public static final double DEFAULT_SCALE = 100.0; + + public static final int MASTER = 0; + + protected List<String> gaugeNames; + + protected String riverName; + + protected double scale; + + protected int kind; + + protected Map<String, double [][]> values; + + public DischargeTables() { + } + + public DischargeTables(String riverName, String gaugeName) { + this(riverName, gaugeName, MASTER); + } + + public DischargeTables(String riverName, String gaugeName, int kind) { + this(riverName, new String [] { gaugeName }, kind); + } + + public DischargeTables(String riverName, String [] gaugeNames) { + this(riverName, gaugeNames, MASTER); + } + + public DischargeTables(String riverName, String [] gaugeNames, int kind) { + this(riverName, Arrays.asList(gaugeNames), kind); + } + + public DischargeTables( + String riverName, + List<String> gaugeNames, + int kind + ) { + scale = Double.NaN; + this.kind = kind; + this.riverName = riverName; + this.gaugeNames = gaugeNames; + } + + public double [][] getFirstTable() { + return getFirstTable(DEFAULT_SCALE); + } + + public double [][] getFirstTable(double scale) { + Map<String, double [][]> values = getValues(scale); + for (double [][] table: values.values()) { + return table; + } + return null; + } + + public Map<String, double [][]> getValues() { + return getValues(DEFAULT_SCALE); + } + + public Map<String, double [][]> getValues(double scale) { + if (values == null || scale != this.scale) { + values = loadValues(scale); + this.scale = scale; + } + return values; + } + + protected Map<String, double [][]> loadValues(double scale) { + Map<String, double [][]> values = new HashMap<String, double [][]>(); + + Session session = SessionHolder.HOLDER.get(); + + Query gaugeQuery = session.createQuery( + "from Gauge where name=:gauge and river.name=:river"); + gaugeQuery.setParameter("river", riverName); + + for (String gaugeName: gaugeNames) { + gaugeQuery.setParameter("gauge", gaugeName); + List<Gauge> gauges = gaugeQuery.list(); + if (gauges.isEmpty()) { + log.warn( + "no gauge '"+gaugeName+"' at river '"+riverName+"'"); + continue; + } + Gauge gauge = gauges.get(0); + + List<DischargeTable> tables = gauge.getDischargeTables(); + + if (tables.isEmpty()) { + log.warn( + "no discharge table for gauge '" + gaugeName + "'"); + continue; + } + + // TODO: Filter by time interval + DischargeTable table = tables.get(0); + + List<DischargeTableValue> dtvs = + table.getDischargeTableValues(); + + final double [][] vs = new double[2][dtvs.size()]; + + boolean qSorted = true; + + double lastQ = -Double.MAX_VALUE; + + int idx = 0; + for (DischargeTableValue dtv: dtvs) { + double q = dtv.getQ().doubleValue(); + vs[0][idx] = q * scale; + vs[1][idx] = dtv.getW().doubleValue() * scale; + ++idx; + + if (qSorted && lastQ > q) { + qSorted = false; + } + lastQ = q; + } + + if (!qSorted) { + log.debug("need to sort by q values"); + // TODO: Do this db level. + // XXX: This is so ugly :-( + Integer [] indices = new Integer[vs[0].length]; + for (int i = 0; i < indices.length; ++i) { + indices[i] = i; + } + + Arrays.sort(indices, new Comparator<Integer>() { + public int compare(Integer a, Integer b) { + double va = vs[1][a]; + double vb = vs[1][b]; + double d = va - vb; + if (d < 0.0) return -1; + if (d > 0.0) return +1; + return 0; + } + }); + + double [][] vs2 = new double[2][vs[0].length]; + for (int i = 0; i < indices.length; ++i) { + vs2[0][i] = vs[0][indices[i]]; + vs2[1][i] = vs[1][indices[i]]; + } + values.put(gaugeName, vs2); + } + else { + values.put(gaugeName, vs); + } + + } + + return values; + } + + public static double getQForW(double [][] values, double w) { + + boolean debug = log.isDebugEnabled(); + + if (debug) { + log.debug("calculating getQForW(" + w + ")"); + } + + int index = Arrays.binarySearch(values[0], w); + if (index >= 0) { + return values[0][index]; + } + + index = -index - 1; // insert position + + if (index < 1 || index >= values[0].length) { + // do not extraploate + if (debug) { + log.debug("we do not extrapolate: NaN"); + } + return Double.NaN; + } + + double w1 = values[0][index-1]; + double w2 = values[0][index ]; + double q1 = values[1][index-1]; + double q2 = values[1][index ]; + + // q1 = m*w1 + b + // q2 = m*w2 + b + // q2 - q1 = m*(w2 - w1) + // m = (q2 - q1)/(w2 - w1) # w2 != w1 + // b = q1 - m*w1 + + double q; + if (w1 == w2) { + q = 0.5*(q1 + q2); + if (debug) { + log.debug("same w1 and w1: " + w1); + } + } + else { + double m = (q2 - q1)/(w2 - w1); + double b = q1 - m*w1; + q = w*m + b; + } + if (debug) { + log.debug("Q(" + w + ") = " + q); + } + return q; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,57 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; +import java.util.ArrayList; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.River; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.Range; + +import org.hibernate.Session; +import org.hibernate.Query; + +public class GaugesFactory +{ + public static List<Gauge> getGauges(River river) { + return getGauges(river.getName()); + } + + public static List<Gauge> getGauges(String river) { + Session session = SessionHolder.HOLDER.get(); + Query query = session.createQuery( + "from Gauge where river.name=:name"); + query.setParameter("name", river); + return query.list(); + } + + public static List<Gauge> filterRanges( + List<Gauge> gauges, + List<double []> ranges + ) { + // XXX: Inefficent! + ArrayList<Range> rs = new ArrayList<Range>(); + for (double [] range: ranges) { + double a = range[0]; + double b = range[1]; + rs.add(new Range(Math.min(a, b), Math.max(a, b), null)); + } + return filter(gauges, rs); + } + + public static List<Gauge> filter(List<Gauge> gauges, List<Range> ranges) { + // TODO: Make it an HQL filter! + ArrayList<Gauge> out = new ArrayList<Gauge>(); + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + for (Range cmp: ranges) { + if (range.intersects(cmp)) { + out.add(gauge); + break; + } + } + } + return out; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,80 @@ +package de.intevation.flys.artifacts.model; + +import java.math.BigDecimal; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.hibernate.Session; +import org.hibernate.Query; + +import de.intevation.flys.backend.SessionHolder; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.MainValue; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class MainValuesFactory { + + private static Logger logger = Logger.getLogger(MainValuesFactory.class); + + public static List<MainValue> getMainValues(Gauge gauge) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "from MainValue where gauge=:gauge"); + query.setParameter("gauge", gauge); + + return query.list(); + } + + + /** + * Returns an array of [days, qs] necessary to create duration curves. + * + * @param gauge The selected gauge. + * + * @return a 2dim array of [days, qs] where days is an int[] and qs is + * an double[]. + */ + public static Object[] getDurationCurveData(Gauge gauge) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "select cast(nmv.name as integer) as days, mv.value as q " + + "from MainValue as mv " + + "join mv.mainValue as nmv " + + "join nmv.type mvt " + + "where mvt.name = 'D' and mv.gauge.id = :gauge_id " + + "order by days"); + + query.setParameter("gauge_id", gauge.getId()); + + List<Object> results = query.list(); + int[] days = new int[results.size()]; + double[] qs = new double[results.size()]; + + int idx = 0; + + for (Object obj: results) { + Object[] arr = (Object[]) obj; + + try { + int day = ((Integer) arr[0]).intValue(); + double q = ((BigDecimal) arr[1]).doubleValue(); + + days[idx] = day; + qs[idx++] = q; + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + return new Object[] { days, qs }; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,57 @@ +package de.intevation.flys.artifacts.model; + +import de.intevation.artifactdatabase.state.DefaultFacet; + + +public class ManagedFacet extends DefaultFacet { + + /** The uuid of the owner artifact.*/ + protected String uuid; + + /** A property that determines the position of this facet.*/ + protected int position; + + /** A property that determines if this facet is active or not.*/ + protected int active; + + + public ManagedFacet( + String name, + String desc, + String uuid, + int pos, + int active) + { + super(name, desc); + + this.uuid = uuid; + this.position = pos; + this.active = active; + } + + + public void setPosition(int pos) { + this.position = pos; + } + + + public int getPosition() { + return position; + } + + + public void setActive(int active) { + this.active = active; + } + + + public int getActive() { + return active; + } + + + public String getArtifact() { + return uuid; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RangeWithValues.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,41 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class RangeWithValues implements Serializable { + + protected double lower; + protected double upper; + protected double[] values; + + + public RangeWithValues() { + } + + + public RangeWithValues(double lower, double upper, double[] values) { + this.lower = lower; + this.upper = upper; + this.values = values; + } + + + public double getLower() { + return lower; + } + + + public double getUpper() { + return upper; + } + + + public double[] getValues() { + return values; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,52 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.River; + +import org.hibernate.Query; +import org.hibernate.Session; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class RiverFactory { + + /** We don't need to instantiate concrete objects of this class. */ + private RiverFactory() { + } + + + /** + * Returns all rivers that were found in the backend. + * + * @return all rivers. + */ + public static List<River> getRivers() { + Session session = SessionHolder.HOLDER.get(); + return (List<River>)session + .createQuery("from River order by name").list(); + } + + + /** + * Returns a River object fetched from database based on its name. + * + * @param river The name of a river. + * + * @return the River object. + */ + public static River getRiver(String river) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "from River where name =:name"); + query.setParameter("name", river); + + List<River> rivers = query.list(); + + return (rivers != null && rivers.size() > 0) ? rivers.get(0) : null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQCKms.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,70 @@ +package de.intevation.flys.artifacts.model; + +import gnu.trove.TDoubleArrayList; + + +/** + * This class represents a pool of data triples that consists of 'W', 'Q' and + * 'KM' data with corrected 'W' values computed by a BackJumpCorrector. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQCKms extends WQKms { + + protected TDoubleArrayList cw; + + + public WQCKms(double[] kms, double[] qs, double[] ws, double[] cws) { + super(kms, qs, ws); + + this.cw = new TDoubleArrayList(cws); + } + + + /** + * Adds a new row to this data pool with corrected W. + * + * @param w a W. + * @param q a Q. + * @param kms a Kms. + * @param cw The corrected W. + */ + public void add(double w, double q, double kms, double cw) { + add(w, q, kms); + this.cw.add(cw); + } + + + /** + * This method returns a 4dim array of W, Q,Kms and corrected W. + * + * @param idx The position of the triple. + * @param dst destination array + * + * @return a 4dim array of [W, Q, Kms, CW] in dst. + */ + public double[] get(int idx, double[] dst) { + dst = super.get(idx, dst); + + if (dst.length < 4) { + return dst; + } + + if (cw != null && cw.size() > idx) { + dst[3] = cw.get(idx); + } + + return dst; + } + + + /** + * Returns the double array of corrected W values. + * + * @return the double array of corrected W values. + */ + public double[] getCWs() { + return cw.toNativeArray(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQDay.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,64 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +import gnu.trove.TDoubleArrayList; +import gnu.trove.TIntArrayList; + + +/** + * This class represents a pool of data triples that consists of 'W', 'Q' and + * 'Day' data. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQDay implements Serializable { + + protected TIntArrayList days; + + protected TDoubleArrayList ws; + + protected TDoubleArrayList qs; + + + public WQDay() { + days = new TIntArrayList(); + ws = new TDoubleArrayList(); + qs = new TDoubleArrayList(); + } + + + public WQDay(int capacity) { + days = new TIntArrayList(capacity); + ws = new TDoubleArrayList(capacity); + qs = new TDoubleArrayList(capacity); + } + + + public void add(int day, double w, double q) { + days.add(day); + ws.add(w); + qs.add(q); + } + + + public int size() { + return days.size(); + } + + + public int getDay(int idx) { + return days.get(idx); + } + + + public double getW(int idx) { + return ws.get(idx); + } + + + public double getQ(int idx) { + return qs.get(idx); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKms.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,158 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +import gnu.trove.TDoubleArrayList; + +import org.apache.log4j.Logger; + + +/** + * This class represents a pool of data triples that consists of 'W', 'Q' and + * 'KM' data. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQKms implements Serializable { + + private static Logger logger = Logger.getLogger(WQKms.class); + + + /** The array that contains the 'W' values.*/ + protected TDoubleArrayList w; + + /** The array that contains the 'Q' values.*/ + protected TDoubleArrayList q; + + /** The array that contains the 'KMs' values.*/ + protected TDoubleArrayList kms; + + + public WQKms() { + this.w = new TDoubleArrayList(); + this.q = new TDoubleArrayList(); + this.kms = new TDoubleArrayList(); + } + + public WQKms(int capacity) { + this.w = new TDoubleArrayList(capacity); + this.q = new TDoubleArrayList(capacity); + this.kms = new TDoubleArrayList(capacity); + } + + + public WQKms(double[] kms, double[] qs, double[] ws) { + this.w = new TDoubleArrayList(ws); + this.q = new TDoubleArrayList(qs); + this.kms = new TDoubleArrayList(kms); + } + + + /** + * Adds a new row to this data pool. + * + * @param w a W. + * @param q a Q. + * @param kms a Kms. + */ + public void add(double w, double q, double kms) { + this.w.add(w); + this.q.add(q); + this.kms.add(kms); + } + + + public void add(double[] w, double[] q, double[] kms) { + this.w.add(w); + this.q.add(q); + this.kms.add(kms); + } + + + /** + * Returns the number of triples stored in this data pool. + * + * @return the number of triples stored in this data pool. + */ + public int size() { + return kms.size(); + } + + /** + * This method returns a triple of W, Q and Kms in a single 3dim array. + * + * @param idx The position of the triple. + * @param dst destination array + * + * @return a triple of [W, Q, Kms] in dst. + */ + public double[] get(int idx, double [] dst) { + dst[0] = w .get(idx); + dst[1] = q .get(idx); + dst[2] = kms.get(idx); + return dst; + } + + + public double getKms(int idx) { + return kms.get(idx); + } + + + public double[] getKms() { + return kms.toNativeArray(); + } + + + public double[] getWs() { + return w.toNativeArray(); + } + + + public double[] getQs() { + return q.toNativeArray(); + } + + + /** + * Returns a string that consist of the first and last kilometer. + * + * @return a string that consist of the first and last kilometer. + */ + public String toString() { + double from = getKms(0); + double to = getKms(size()-1); + return Double.toString(from) + " - " + Double.toString(to); + } + + + /** + * Merges the WQKms objects of an incoming 2dim array (table) where the + * objects of a column belong together. + * + * @param toMerge The objects that need to be merged. + * + * @return an array of merged WQKms objects. + */ + public static WQKms[] merge(WQKms[][] toMerge) { + int num = toMerge[0].length; + + // TODO IS THE LENGTH CORRECT? + WQKms[] merged = new WQKms[num]; + for (int i = 0; i < num; i++) { + merged[i] = new WQKms(); + } + + for (int i = 0; i < toMerge.length; i++) { + WQKms[] tmp = toMerge[i]; + + for (int j = 0; j < num; j++) { + WQKms toAdd = tmp[j]; + merged[j].add(toAdd.getWs(), toAdd.getQs(), toAdd.getKms()); + } + } + + return merged; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,41 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.River; +import de.intevation.flys.model.Wst; + +import org.hibernate.Query; +import org.hibernate.Session; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WstFactory { + + /** We don't need to instantiate concrete objects of this class. */ + private WstFactory() { + } + + + /** + * Returns the Wst object for a given river. + * + * @param river The river. + * + * @return the Wst of <i>river</i>. + */ + public static Wst getWst(River river) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "from Wst where river=:river"); + query.setParameter("river", river); + + List<Wst> wst = query.list(); + + return (wst != null && wst.size() > 0) ? wst.get(0) : null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,817 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +import de.intevation.flys.model.River; +import de.intevation.flys.model.Wst; +import de.intevation.flys.model.WstColumn; + +import de.intevation.flys.artifacts.math.LinearRemap; + +import de.intevation.flys.artifacts.cache.CacheFactory; + +import de.intevation.flys.backend.SessionHolder; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Collections; + +import org.apache.log4j.Logger; + +import org.apache.commons.math.analysis.interpolation.SplineInterpolator; + +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; + +import org.apache.commons.math.ArgumentOutsideDomainException; + +import org.hibernate.Session; +import org.hibernate.Query; +import org.hibernate.SQLQuery; + +import org.hibernate.type.StandardBasicTypes; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.Element; + +public class WstValueTable +implements Serializable +{ + private static Logger log = Logger.getLogger(WstValueTable.class); + + public static final String CACHE_NAME = "wst-value-table"; + + // TODO: put this into a property file + public static final String SQL_POS_WQ = + "SELECT position, w, q, column_pos" + + " FROM wst_value_table" + + " WHERE wst_id = :wst_id"; + + public static final int DEFAULT_Q_STEPS = 500; + + public static class Column + implements Serializable + { + protected String name; + + public Column() { + } + + public Column(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + // class Column + + public static class QPosition { + + protected double q; + protected int index; + protected double weight; + + public QPosition() { + } + + public QPosition(double q, int index, double weight) { + this.index = index; + this.weight = weight; + } + + } // class Position + + public static class Row + implements Serializable, Comparable<Row> + { + double km; + double [] ws; + double [] qs; + boolean qSorted; + + public Row() { + } + + public Row(double km) { + this.km = km; + } + + public Row(double km, double [] ws, double [] qs) { + this(km); + this.ws = ws; + this.qs = qs; + } + + public static final double linear( + double x, + double x1, double x2, + double y1, double y2 + ) { + // y1 = m*x1 + b + // y2 = m*x2 + b + // y2 - y1 = m*(x2 - x1) + // m = (y2 - y1)/(x2 - x1) # x2 != x1 + // b = y1 - m*x1 + + if (x1 == x2) { + return 0.5*(y1 + y2); + } + double m = (y2 - y1)/(x2 - x1); + double b = y1 - m*x1; + return x*m + b; + } + + public static final double factor(double x, double p1, double p2) { + // 0 = m*p1 + b <=> b = -m*p1 + // 1 = m*p2 + b + // 1 = m*(p2 - p1) + // m = 1/(p2 - p1) # p1 != p2 + // f(x) = x/(p2-p1) - p1/(p2-p1) <=> (x-p1)/(p2-p1) + + return p1 == p2 ? 0.0 : (x-p1)/(p2-p1); + } + + public static final double weight(double factor, double a, double b) { + return (1.0-factor)*a + factor*b; + } + + public double getWForKM(Row other, int index, double km) { + double w1 = ws[index]; + double w2 = other.ws[index]; + double km1 = this.km; + double km2 = other.km; + return linear(km, km1, km2, w1, w2); + } + + public void interpolateW( + Row other, + double km, + double [] input, + double [] output + ) { + double factor = factor(km, this.km, other.km); + + for (int i = 0; i < input.length; ++i) { + double q = input[i]; + int idx1 = getQIndex(q); + int idx2 = other.getQIndex(q); + + double w1 = idx1 >= 0 + ? ws[idx1] + : interpolateW(-idx1-1, q); + + double w2 = idx2 >= 0 + ? other.ws[idx2] + : other.interpolateW(-idx2-1, q); + + output[i] = weight(factor, w1, w2); + } + } + + public double interpolateW(int idx, double q) { + return idx < 1 || idx >= qs.length + ? Double.NaN // do not extrapolate + : linear(q, qs[idx-1], qs[idx], ws[idx-1], ws[idx]); + } + + public double interpolateW(double q) { + if (Double.isNaN(q)) return Double.NaN; + int index = getQIndex(q); + return index >= 0 ? ws[index] : interpolateW(-index -1, q); + } + + public int getQIndex(double q) { + return qSorted ? binaryQIndex(q) : linearQIndex(q); + } + + public int binaryQIndex(double q) { + return Arrays.binarySearch(qs, q); + } + + public int linearQIndex(double q) { + switch (qs.length) { + case 0: break; + case 1: + if (qs[0] == q) return 0; + if (qs[0] < q) return -(1+1); + return -(0+1); + default: + for (int i = 1; i < qs.length; ++i) { + double qa = qs[i-1]; + double qb = qs[i]; + if (qa == q) return i-1; + if (qb == q) return i; + if (qa > qb) { double t = qa; qa = qb; qb = t; } + if (q > qa && q < qb) return -(i+1); + } + return -qs.length; + } + + return -1; + } + + public int compareTo(Row other) { + double d = km - other.km; + if (d < 0.0) return -1; + if (d > 0.0) return +1; + return 0; + } + + public double [][] cloneWQs() { + return new double [][] { + (double [])ws.clone(), + (double [])qs.clone() }; + } + + public double [][] interpolateWQ(Row other, double km, int steps) { + + int W = Math.min(ws.length, other.ws.length); + + if (W < 1) { + return new double[2][0]; + } + + double factor = factor(km, this.km, other.km); + + double [] splineQ = new double[W]; + double [] splineW = new double[W]; + + double minQ = Double.MAX_VALUE; + double maxQ = -Double.MAX_VALUE; + + for (int i = 0; i < W; ++i) { + double wws = weight(factor, ws[i], other.ws[i]); + double wqs = weight(factor, qs[i], other.qs[i]); + splineW[i] = wws; + splineQ[i] = wqs; + if (wqs < minQ) minQ = wqs; + if (wqs > maxQ) maxQ = wqs; + } + + double stepWidth = (maxQ - minQ)/steps; + + SplineInterpolator interpolator = new SplineInterpolator(); + PolynomialSplineFunction spline = + interpolator.interpolate(splineQ, splineW); + + double [] outWs = new double[steps]; + double [] outQs = new double[steps]; + + try { + double q = minQ; + for (int i = 0; i < outWs.length; ++i, q += stepWidth) { + outWs[i] = spline.value(outQs[i] = q); + } + } + catch (ArgumentOutsideDomainException aode) { + log.error("Spline interpolation failed.", aode); + } + + return new double [][] { outWs, outQs }; + } + + public double [][] interpolateWQ(int steps) { + + int W = ws.length; + + if (W < 1) { + return new double[2][0]; + } + + double [] splineW = new double[W]; + double [] splineQ = new double[W]; + + double minQ = Double.MAX_VALUE; + double maxQ = -Double.MAX_VALUE; + + for (int i = 0; i < W; ++i) { + splineW[i] = ws[i]; + splineQ[i] = qs[i]; + if (qs[i] < minQ) minQ = qs[i]; + if (qs[i] > maxQ) maxQ = qs[i]; + } + + double stepWidth = (maxQ - minQ)/steps; + + SplineInterpolator interpolator = new SplineInterpolator(); + + PolynomialSplineFunction spline = + interpolator.interpolate(splineQ, splineW); + + double [] outWs = new double[steps]; + double [] outQs = new double[steps]; + + try { + double q = minQ; + for (int i = 0; i < outWs.length; ++i, q += stepWidth) { + outWs[i] = spline.value(outQs[i] = q); + } + } + catch (ArgumentOutsideDomainException aode) { + log.error("Spline interpolation failed.", aode); + } + + return new double [][] { outWs, outQs }; + } + + public int ascendingWs() { + if (ws.length < 2) { + return ws.length; + } + + int idx = 1; + + for (; idx < ws.length; ++idx) { + if (Double.isNaN(ws[idx]) || ws[idx] < ws[idx-1]) { + return idx; + } + } + + return idx; + } + + public double [][] weightWQs(Row other, double km) { + int W = Math.min(ws.length, other.ws.length); + double factor = factor(km, this.km, other.km); + + double [] outWs = new double[W]; + double [] outQs = new double[W]; + + for (int i = 0; i < W; ++i) { + outWs[i] = weight(factor, ws[i], other.ws[i]); + outQs[i] = weight(factor, qs[i], other.qs[i]); + } + + return new double [][] { outWs, outQs }; + } + + public QPosition getQPosition(double q) { + int qIndex = getQIndex(q); + + if (qIndex >= 0) { + // direct column match + return new QPosition(q, qIndex, 1.0); + } + + qIndex = -qIndex -1; + + if (qIndex < 0 || qIndex >= qs.length-1) { + // do not extrapolate + return null; + } + + return new QPosition( + q, qIndex, factor(q, qs[qIndex-1], qs[qIndex])); + } + + public QPosition getQPosition(Row other, double km, double q) { + + QPosition qpt = getQPosition(q); + QPosition qpo = other.getQPosition(q); + + if (qpt == null || qpo == null) { + return null; + } + + double kmWeight = factor(km, this.km, other.km); + + // XXX: Index interpolation is a bit sticky here! + + int index = (int)Math.round( + weight(kmWeight, qpt.index, qpo.index)); + + double weight = weight(kmWeight, qpt.weight, qpo.weight); + + return new QPosition(q, index, weight); + } + + public void storeWQ( + QPosition qPosition, + double [] ows, + double [] oqs, + int index + ) { + int qIdx = qPosition.index; + double qWeight = qPosition.weight; + + if (qWeight == 1.0) { + oqs[index] = qs[qIdx]; + ows[index] = ws[qIdx]; + } + else { + oqs[index] = weight(qWeight, qs[qIdx-1], qs[qIdx]); + ows[index] = weight(qWeight, ws[qIdx-1], ws[qIdx]); + } + } + + public void storeWQ( + Row other, + double km, + QPosition qPosition, + double [] ows, + double [] oqs, + int index + ) { + double kmWeight = factor(km, this.km, other.km); + + double tq, tw; + double oq, ow; + + int qIdx = qPosition.index; + double qWeight = qPosition.weight; + + if (qWeight == 1.0) { + tq = qs[qIdx]; + tw = ws[qIdx]; + oq = other.qs[qIdx]; + ow = other.ws[qIdx]; + } + else { + tq = weight(qWeight, qs[qIdx-1], qs[qIdx]); + tw = weight(qWeight, ws[qIdx-1], ws[qIdx]); + oq = weight(qWeight, other.qs[qIdx-1], other.qs[qIdx]); + ow = weight(qWeight, other.ws[qIdx-1], other.ws[qIdx]); + } + + oqs[index] = weight(kmWeight, oq, tq); + ows[index] = weight(kmWeight, ow, tw); + } + + public void storeWQ( + QPosition qPosition, + LinearRemap remap, + double [] ows, + double [] oqs, + int index + ) { + int qIdx = qPosition.index; + double qWeight = qPosition.weight; + + oqs[index] = remap.eval(km, qWeight == 1.0 + ? qs[qIdx] + : weight(qWeight, qs[qIdx-1], qs[qIdx])); + + ows[index] = interpolateW(oqs[index]); + } + + public void storeWQ( + Row other, + double km, + QPosition qPosition, + LinearRemap remap, + double [] ows, + double [] oqs, + int index + ) { + double kmWeight = factor(km, this.km, other.km); + + double tq, tw; + double oq, ow; + + int qIdx = qPosition.index; + double qWeight = qPosition.weight; + + if (qWeight == 1.0) { + tq = remap.eval(this.km, qs[qIdx]); + oq = remap.eval(other.km, other.qs[qIdx]); + } + else { + tq = remap.eval( + this.km, + weight(qWeight, qs[qIdx-1], qs[qIdx])); + oq = remap.eval( + other.km, + weight(qWeight, other.qs[qIdx-1], other.qs[qIdx])); + } + + tw = interpolateW(tq); + ow = other.interpolateW(oq); + + oqs[index] = weight(kmWeight, oq, tq); + ows[index] = weight(kmWeight, ow, tw); + } + } + // class Row + + protected List<Row> rows; + + protected Column [] columns; + + public WstValueTable() { + rows = new ArrayList<Row>(); + } + + public WstValueTable(Column [] columns) { + this(); + this.columns = columns; + } + + public double [] interpolateW(double km, double [] qs) { + return interpolateW(km, qs, new double[qs.length]); + } + + public double [] interpolateW(double km, double [] qs, double [] ws) { + + int rowIndex = Collections.binarySearch(rows, new Row(km)); + + if (rowIndex >= 0) { // direct row match + Row row = rows.get(rowIndex); + for (int i = 0; i < qs.length; ++i) { + ws[i] = row.interpolateW(qs[i]); + } + } + else { // needs bilinear interpolation + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex >= rows.size()) { + // do not extrapolate + Arrays.fill(ws, Double.NaN); + } + else { + rows.get(rowIndex-1).interpolateW( + rows.get(rowIndex), + km, qs, ws); + } + } + + return ws; + } + + + public double [][] interpolateWQ(double km) { + return interpolateWQ(km, DEFAULT_Q_STEPS); + } + + public double [][] interpolateWQ(double km, int steps) { + + int rowIndex = Collections.binarySearch(rows, new Row(km)); + + if (rowIndex >= 0) { // direct row match + Row row = rows.get(rowIndex); + return row.interpolateWQ(steps); + } + + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex >= rows.size()) { + // do not extrapolate + return new double[2][0]; + } + + Row r1 = rows.get(rowIndex-1); + Row r2 = rows.get(rowIndex); + + return r1.interpolateWQ(r2, km, steps); + } + + public void interpolate( + double [] kms, + double [] ws, + double [] qs, + QPosition qPosition, + LinearRemap remap + ) { + int R1 = rows.size()-1; + + Row kmKey = new Row(); + for (int i = 0; i < kms.length; ++i) { + kmKey.km = kms[i]; + int rowIndex = Collections.binarySearch(rows, kmKey); + + if (rowIndex >= 0) { + // direct row match + rows.get(rowIndex).storeWQ(qPosition, remap, ws, qs, i); + continue; + } + + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex >= R1) { + // do not extrapolate + ws[i] = qs[i] = Double.NaN; + continue; + } + Row r1 = rows.get(rowIndex-1); + Row r2 = rows.get(rowIndex); + r1.storeWQ(r2, kms[i], qPosition, remap, ws, qs, i); + } + } + + public QPosition interpolate( + double q, + int referenceIndex, + double [] kms, + double [] ws, + double [] qs + ) { + Row kmKey = new Row(kms[referenceIndex]); + + int rowIndex = Collections.binarySearch(rows, kmKey); + + int R1 = rows.size()-1; + + QPosition qPosition = null; + + if (rowIndex >= 0) { // direct row match + qPosition = rows.get(rowIndex).getQPosition(q); + } + else { + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex >= R1) { + // do not extrapolate + return null; + } + + // interpolation case + Row r1 = rows.get(rowIndex-1); + Row r2 = rows.get(rowIndex); + + qPosition = r1.getQPosition(r2, kms[referenceIndex], q); + } + + if (qPosition == null) { + // we cannot locate q in reference row + return null; + } + + for (int i = 0; i < kms.length; ++i) { + kmKey.km = kms[i]; + rowIndex = Collections.binarySearch(rows, kmKey); + + if (rowIndex >= 0) { + // direct row match + rows.get(rowIndex).storeWQ(qPosition, ws, qs, i); + continue; + } + + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex >= R1) { + // do not extrapolate + ws[i] = qs[i] = Double.NaN; + continue; + } + Row r1 = rows.get(rowIndex-1); + Row r2 = rows.get(rowIndex); + r1.storeWQ(r2, kms[i], qPosition, ws, qs, i); + } + + return qPosition; + } + + public static WstValueTable getTable(River river) { + return getTable(river, 0); + } + + public static final class CacheKey + implements Serializable + { + private int riverId; + private int kind; + + public CacheKey(int riverId, int kind) { + this.riverId = riverId; + this.kind = kind; + } + + public int hashCode() { + return (riverId << 8) | kind; + } + + public boolean equals(Object other) { + if (!(other instanceof CacheKey)) { + return false; + } + CacheKey o = (CacheKey)other; + return riverId == o.riverId && kind == o.kind; + } + } // class CacheKey + + public static WstValueTable getTable(River river, int kind) { + + Cache cache = CacheFactory.getCache(CACHE_NAME); + + CacheKey cacheKey; + + if (cache != null) { + cacheKey = new CacheKey(river.getId(), kind); + Element element = cache.get(cacheKey); + if (element != null) { + log.debug("got wst value table from cache"); + return (WstValueTable)element.getValue(); + } + } + else { + cacheKey = null; + } + + WstValueTable valueTable = getTableUncached(river, kind); + + if (cacheKey != null) { + log.debug("store wst value in cache"); + Element element = new Element(cacheKey, valueTable); + cache.put(element); + } + + return valueTable; + } + + public static WstValueTable getTableUncached(River river, int kind) { + + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "from Wst where river=:river and kind=:kind"); + query.setParameter("river", river); + query.setInteger("kind", kind); + + List<Wst> wsts = query.list(); + + if (wsts.isEmpty()) { + return null; + } + + Wst wst = wsts.get(0); + + // TODO: Do this sorting at database level + List<WstColumn> wstColumns = new ArrayList(wst.getColumns()); + Collections.sort(wstColumns, new Comparator<WstColumn>() { + public int compare(WstColumn a, WstColumn b) { + int pa = a.getPosition(); + int pb = b.getPosition(); + if (pa < pb) return -1; + if (pa > pb) return +1; + return 0; + } + }); + + Column [] columns = new Column[wstColumns.size()]; + for (int i = 0; i < columns.length; ++i) { + columns[i] = new Column(wstColumns.get(i).getName()); + } + + // using native SQL here to avoid myriad of small objects. + SQLQuery sqlQuery = session.createSQLQuery(SQL_POS_WQ) + .addScalar("position", StandardBasicTypes.DOUBLE) + .addScalar("w", StandardBasicTypes.DOUBLE) + .addScalar("q", StandardBasicTypes.DOUBLE) + .addScalar("column_pos", StandardBasicTypes.INTEGER); + + sqlQuery.setInteger("wst_id", wst.getId()); + + WstValueTable valueTable = new WstValueTable(columns); + + int lastColumnNo = -1; + Row row = null; + + Double lastQ = -Double.MAX_VALUE; + boolean qSorted = true; + + for (Object r: sqlQuery.list()) { + Object[] result = (Object[]) r; + + double km = (Double) result[0]; + Double w = (Double) result[1]; + Double q = (Double) result[2]; + int columnNo = (Integer)result[3]; + + if (columnNo > lastColumnNo) { // new row + if (row != null) { + row.qSorted = qSorted; + valueTable.rows.add(row); + } + row = new Row( + km, + new double[columnNo+1], + new double[columnNo+1]); + lastQ = -Double.MAX_VALUE; + qSorted = true; + } + + row.ws[columnNo] = w != null ? w : Double.NaN; + row.qs[columnNo] = q != null ? q : Double.NaN; + + if (qSorted && (q == null || lastQ > q)) { + qSorted = false; + } + lastQ = q; + + lastColumnNo = columnNo; + } + + if (row != null) { + valueTable.rows.add(row); + } + + return valueTable; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/resources/Resources.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,144 @@ +package de.intevation.flys.artifacts.resources; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallMeta; + +/** + * This class provides methods for i18n. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class Resources { + + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(Resources.class); + + /** The singleton instance.*/ + private static Resources INSTANCE; + + /** The locales supported by this server.*/ + protected Locale[] locales; + + /** + * No instance of this class is necessary. + */ + private Resources() { + } + + + /** + * Returns the locales supported by this server. + * + * @return the supported locales. + */ + public Locale[] getLocales() { + if (locales == null) { + readLocales(); + } + + return locales; + } + + + /** + * Read the locales configured for this server. + */ + protected void readLocales() { + // TODO IMPLEMENT ME + + locales = new Locale[2]; + locales[0] = Locale.GERMANY; + locales[1] = Locale.ENGLISH; + } + + + public static Locale getLocale(CallMeta meta) { + if (INSTANCE == null) { + INSTANCE = new Resources(); + } + + Locale[] locales = INSTANCE.getLocales(); + return meta.getPreferredLocale(locales); + } + + + /** + * This method returns the translated value for <i>key</i> or <i>def</i> if + * <i>key</i> is not existing in the resource bundle. + * + * @param meta The CallMeta object of the request that contains the + * preferred locale. + * @param key The key that should be translated. + * @param def A default value that is returned, if <i>key</i> was not found. + * + * @return the translated message. + */ + public static String getMsg(CallMeta meta, String key, String def) { + if (INSTANCE == null) { + INSTANCE = new Resources(); + } + + Locale[] locales = INSTANCE.getLocales(); + Locale locale = meta.getPreferredLocale(locales); + + return getMsg(locale, key, def); + } + + + /** + * Returns a translated message based on a template specified by <i>key</i> + * that has necessary values to be filled in. + * + * @param meta The CallMeta object. + * @param key The key of the template in the resource bundle. + * @param def the default value if no template was found with <i>key</i>. + * @param args The arguments that are necessary for the template. + * + * @return a translated string. + */ + public static String getMsg( + CallMeta meta, + String key, + String def, + Object[] args) + { + String template = getMsg(meta, key, null); + + if (template == null) { + return def; + } + + return MessageFormat.format(template, args); + } + + + /** + * This method returns the translated value for <i>key</i> or <i>def</i> if + * <i>key</i> is not existing in the resource bundle. + * + * @param locale The locale. + * @param key The key that should be translated. + * @param def A default value that is returned, if <i>key</i> was not found. + * + * @return the translated message. + */ + public static String getMsg(Locale locale, String key, String def) { + ResourceBundle bundle = ResourceBundle.getBundle("messages", locale); + + try { + return bundle.getString(key); + } + catch (MissingResourceException mre) { + logger.warn("No message found for key: " + key); + + return def; + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,126 @@ +package de.intevation.flys.artifacts.services; + +import java.math.BigDecimal; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.DefaultService; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.Annotation; +import de.intevation.flys.model.Attribute; +import de.intevation.flys.model.Position; +import de.intevation.flys.model.Range; + +import de.intevation.flys.artifacts.model.AnnotationsFactory; + +import org.hibernate.Session; + +/** + * This service provides information about distances of a specified river. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DistanceInfoService extends DefaultService { + + /** The logger used in this service.*/ + private static Logger logger = Logger.getLogger(DistanceInfoService.class); + + + /** + * The default constructor. + */ + public DistanceInfoService() { + } + + + public Document process( + Document data, + Object globalContext, + CallMeta callMeta) + { + logger.debug("DistanceInfoService.process"); + + Document result = XMLUtils.newDocument(); + + String river = XMLUtils.xpathString( + data, "/art:river/text()", ArtifactNamespaceContext.INSTANCE); + + if (river == null || river.trim().length() == 0) { + logger.warn("No river specified. Cannot return distance info!"); + return result; + } + + logger.debug("Search distances for river: " + river); + + ElementCreator ec = new ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Session session = SessionHolder.acquire(); + try { + List<Annotation> annotations = AnnotationsFactory.getAnnotations(river); + + if (annotations == null || annotations.size() == 0) { + logger.warn("No information found for the specified river!"); + return result; + } + + Element all = ec.create("distances"); + + for (Annotation a: annotations) { + Element distance = buildDistanceNode(ec, a); + + if (distance != null) { + all.appendChild(distance); + } + } + + result.appendChild(all); + } + finally { + session.close(); + SessionHolder.release(); + } + + return result; + } + + + /** + * This method build an Element for a distance info. + * + * @param ec The ElementCreator. + * @param anno The Annotation that provides information about the distance. + * + * @return an Element that contains information about a distance. + */ + protected Element buildDistanceNode(ElementCreator ec, Annotation anno) { + Position pos = anno.getPosition(); + Range range = anno.getRange(); + Attribute attr = anno.getAttribute(); + + BigDecimal a = range.getA(); + BigDecimal b = range.getB(); + + Element distance = ec.create("distance"); + ec.addAttr(distance, "description", pos.getValue(), true); + ec.addAttr(distance, "from", a != null ? a.toString() : "", true); + ec.addAttr(distance, "to", b != null ? b.toString() : "", true); + ec.addAttr(distance, "riverside", attr.getValue(), true); + + return distance; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,316 @@ +package de.intevation.flys.artifacts.services; + +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.hibernate.Session; + +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.DefaultService; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.MainValue; +import de.intevation.flys.model.MainValueType; +import de.intevation.flys.model.NamedMainValue; +import de.intevation.flys.model.Range; +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.model.MainValuesFactory; +import de.intevation.flys.artifacts.model.RiverFactory; + + +/** + * This service returns the main values of a river's gauge based on the start + * and end point of the river. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class MainValuesService extends DefaultService { + + /** The logger that is used by this service.*/ + private static Logger logger = Logger.getLogger(MainValuesService.class); + + + /** The XPath that points to the river definition of the incoming request.*/ + public static final String XPATH_RIVER = "/art:mainvalues/art:river/text()"; + + /** The XPath that points to the start definition of the incoming request.*/ + public static final String XPATH_START = "/art:mainvalues/art:end/text()"; + + /** The XPath that points to the end definition of the incoming request.*/ + public static final String XPATH_END = "/art:mainvalues/art:start/text()"; + + /** + * The default constructor. + */ + public MainValuesService() { + } + + + public Document process( + Document data, + Object context, + CallMeta callMeta) + { + logger.debug("MainValuesService.process"); + + try { + River river = getRequestedRiver(data); + double[] minmax = getRequestedStartEnd(data, river); + Gauge gauge = river.determineGauge(minmax[0], minmax[1]); + + List<MainValue> mainValues = getMainValues(river, gauge); + + return buildDocument(river, gauge, mainValues, context); + } + catch (NullPointerException npe) { + logger.error("Could not process the request."); + logger.error(npe, npe); + + return XMLUtils.newDocument(); + } + } + + + /** + * This method extracts the river from the incoming request. If no river + * string was found or no river is found in the database based on this + * string a NullPointerException is thrown. + * + * @param data The incoming request data. + * + * @return the River object. + */ + protected River getRequestedRiver(Document data) + throws NullPointerException + { + logger.debug("MainValuesService.getRiver"); + + String riverStr = XMLUtils.xpathString( + data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE); + + if (riverStr == null || riverStr.trim().length() == 0) { + throw new NullPointerException("No river found in the request."); + } + + River river = RiverFactory.getRiver(riverStr); + + if (river == null) { + throw new NullPointerException("No such river found: " + riverStr); + } + + return river; + } + + + /** + * This method extracts the start and end point from incoming request + * document and returns both values in an array. If no start and end strings + * are found in the document, the min/max values of the <i>river</i> are + * returned. + * + * @param data The incoming request data. + * @param river The river of the request. + * + * @return the start and end point. + */ + protected double[] getRequestedStartEnd(Document data, River river) { + logger.debug("MainValuesService.getStartEnd"); + + String startStr = XMLUtils.xpathString( + data, XPATH_START, ArtifactNamespaceContext.INSTANCE); + + String endStr = XMLUtils.xpathString( + data, XPATH_END, ArtifactNamespaceContext.INSTANCE); + + try { + double start = Double.parseDouble(startStr); + double end = Double.parseDouble(endStr); + + logger.debug("Found start: " + start); + logger.debug("Found end: " + end); + + return new double[] { start, end }; + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + + return river.determineMinMaxDistance(); + } + } + + + /** + * This method creates the result document that includes the main values of + * the specified <i>gauge</i>. + * + * @param river The river. + * @param gauge The gauge. + * + * @return a document that includes the main values of the specified river + * at the specified gauge. + */ + protected List<MainValue> getMainValues(River river, Gauge gauge) + throws NullPointerException + { + if (logger.isDebugEnabled()) { + logger.debug("MainValuesService.buildMainValues"); + logger.debug("River: " + river.getName()); + logger.debug("Gauge: " + gauge.getName()); + } + + Session session = SessionHolder.acquire(); + try { + List<MainValue> mainValues = MainValuesFactory.getMainValues(gauge); + + if (mainValues == null || mainValues.isEmpty()) { + throw new NullPointerException("No main values found."); + } + + logger.debug(mainValues.size() + " main values found."); + + return mainValues; + } + finally { + session.close(); + SessionHolder.release(); + } + } + + + protected Document buildDocument( + River river, + Gauge gauge, + List<MainValue> mainValues, + Object context) + { + logger.debug("MainValuesService.buildDocument"); + + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element rootEl = cr.create("service"); + cr.addAttr(rootEl, "name", "mainvalues"); + + doc.appendChild(rootEl); + + appendMetaInformation(doc, rootEl, river, gauge, context); + appendMainValues(doc, rootEl, mainValues, context); + + return doc; + } + + + /** + * This method appends some meta information to the result document. + * Currently, the river's and gauge's names and the gauge's range are + * appended. + * + * @param root The root element of the result document. + * @param river The river. + * @param gauge The gauge. + * @param context The context object. + */ + protected void appendMetaInformation( + Document doc, + Element root, + River river, + Gauge gauge, + Object context) + { + logger.debug("MainValuesService.appendMetaInformation"); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Range range = gauge.getRange(); + + Element riverEl = cr.create("river"); + cr.addAttr(riverEl, "name", river.getName()); + + Element gaugeEl = cr.create("gauge"); + cr.addAttr(gaugeEl, "name", gauge.getName()); + cr.addAttr(gaugeEl, "from", range.getA().toString()); + cr.addAttr(gaugeEl, "to", range.getB().toString()); + + root.appendChild(riverEl); + root.appendChild(gaugeEl); + } + + + protected void appendMainValues( + Document doc, + Element root, + List<MainValue> mainValues, + Object context) + { + logger.debug("MainValuesService.appendMainValues"); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element list = cr.create("mainvalues"); + + for (MainValue mainValue: mainValues) { + Element newEl = buildMainValueElement(doc, mainValue, context); + + if (newEl != null) { + list.appendChild(newEl); + } + } + + root.appendChild(list); + } + + + /** + * This method builds a concrete mainvalue element. This element consists of + * three attributes: the value, its name and its type. + * + * @param doc The owner document. + * @param mainValue The mainvalue. + * @param context The context object. + * + * @return a mainvalue element. + */ + protected Element buildMainValueElement( + Document doc, + MainValue mainValue, + Object context) + { + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + NamedMainValue namedMainValue = mainValue.getMainValue(); + MainValueType mainValueType = namedMainValue.getType(); + + Element el = cr.create("mainvalue"); + + cr.addAttr(el, "value", mainValue.getValue().toString()); + cr.addAttr(el, "name", namedMainValue.getName()); + cr.addAttr(el, "type", mainValueType.getName()); + + return el; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,129 @@ +package de.intevation.flys.artifacts.services; + +import java.io.InputStream; +import java.io.IOException; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import java.util.Map; +import java.util.HashMap; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.ServiceFactory; + +import de.intevation.artifactdatabase.DefaultService; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; + +import de.intevation.flys.artifacts.services.meta.Builder; + +import de.intevation.flys.backend.SessionHolder; + +import org.hibernate.Session; + +import org.hibernate.jdbc.Work; + +public class MetaDataService +extends DefaultService +{ + private static Logger log = Logger.getLogger(MetaDataService.class); + + public static final String XPATH_RIVER = "/art:river/text()"; + + public static final String META_DATA_TEMPLATE = "/metadata/template.xml"; + + protected Builder builder; + + public MetaDataService() { + } + + protected static Map<String, Object> extractParameters(Document data) { + HashMap<String, Object> parameters = new HashMap<String, Object>(); + + String river = XMLUtils.xpathString( + data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE); + + if (river == null || (river = river.trim()).length() == 0) { + river = "%"; // matches all rivers + } + + parameters.put("river", river); + + return parameters; + } + + @Override + public Document process( + Document data, + Object globalContext, + CallMeta callMeta + ) { + log.debug("MetaDataService.process"); + + final Document result = XMLUtils.newDocument(); + + if (builder == null) { + log.error("MetaDataService is not setup properly."); + return result; + } + + final Map<String, Object> parameters = extractParameters(data); + + Session session = SessionHolder.acquire(); + try { + session.doWork(new Work() { + @Override + public void execute(Connection connection) + throws SQLException + { + log.debug("MetaDataService.execute"); + builder.build(connection, result, parameters); + } + }); + } + finally { + session.close(); + SessionHolder.release(); + } + + return result; + } + + @Override + public void setup(ServiceFactory factory, Object globalContext) { + log.debug("MetaDataService.setup"); + + InputStream in = getClass().getResourceAsStream(META_DATA_TEMPLATE); + + if (in == null) { + log.error("cannot get template resource"); + return; + } + + try { + Document template = XMLUtils.parseDocument(in); + if (template == null) { + log.error("cannot parse meta data template"); + } + else { + builder = new Builder(template); + } + } + finally { + try { + in.close(); + } + catch (IOException ioe) { + log.error(ioe); + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/RiverService.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,80 @@ +package de.intevation.flys.artifacts.services; + +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.DefaultService; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.model.RiverFactory; + +import org.hibernate.Session; + +/** + * This service provides information about the supported rivers by this + * application. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class RiverService extends DefaultService { + + /** The logger used in this service.*/ + private static Logger logger = Logger.getLogger(RiverService.class); + + + /** + * The default constructor. + */ + public RiverService() { + } + + + public Document process( + Document data, + Object globalContext, + CallMeta callMeta) + { + logger.debug("RiverService.process"); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Session session = SessionHolder.acquire(); + try { + List<River> allRivers = RiverFactory.getRivers(); + + Element rivers = ec.create("rivers"); + + for (River river: allRivers) { + Element r = ec.create("river"); + ec.addAttr(r, "name", river.getName(), true); + + rivers.appendChild(r); + } + + result.appendChild(rivers); + } + finally { + session.close(); + SessionHolder.release(); + } + + return result; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,289 @@ +package de.intevation.flys.artifacts.services.meta; + +import java.util.regex.Matcher; + +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; + +import java.sql.SQLException; +import java.sql.Connection; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import org.apache.log4j.Logger; + +public class Builder +{ + private static Logger log = Logger.getLogger(Builder.class); + + public static final String DC_NAMESPACE_URI = + "http://www.intevation.org/2011/Datacage"; + + protected Document template; + + public class BuildHelper + { + protected Document output; + protected StackFrames frames; + protected Connection connection; + protected Map<String, CompiledStatement> statements; + + public BuildHelper( + Document output, + Connection connection, + Map<String, Object> parameters + ) { + this.output = output; + this.connection = connection; + frames = new StackFrames(parameters); + statements = new HashMap<String, CompiledStatement>(); + } + + public void build(List<Node> elements) throws SQLException { + try { + for (Node current: elements) { + build(output, current); + } + } + finally { + for (CompiledStatement cs: statements.values()) { + cs.close(); + } + statements.clear(); + } + } + + protected void context(Node parent, Element current) + throws SQLException + { + NodeList elements = current.getElementsByTagNameNS( + DC_NAMESPACE_URI, "elements"); + + if (elements.getLength() < 1) { + log.warn("no elements found -> ignore"); + return; + } + + NodeList subs = elements.item(0).getChildNodes(); + int S = subs.getLength(); + + if (S < 1) { + log.warn("elements is empty -> ignore"); + return; + } + + NodeList stmntNode = current.getElementsByTagNameNS( + DC_NAMESPACE_URI, "statement"); + + if (stmntNode.getLength() < 1) { + log.warn("dc:context: too less statements"); + return; + } + + String stmntText = stmntNode.item(0).getTextContent(); + + if (stmntText == null + || (stmntText = stmntText.trim()).length() == 0) { + log.warn("dc:context: no sql statement found -> ignored"); + return; + } + + CompiledStatement cs = statements.get(stmntText); + + if (cs == null) { + cs = new CompiledStatement(stmntText); + statements.put(stmntText, cs); + } + + ResultData rd = cs.execute(connection, frames); + + String [] columns = rd.getColumnLabels(); + + + for (Object [] row: rd.getRows()) { + frames.enter(); + try { + frames.put(columns, row); + for (int i = 0; i < S; ++i) { + build(parent, subs.item(i)); + } + } + finally { + frames.leave(); + } + } + } + + protected void element(Node parent, Element current) + throws SQLException + { + String attr = expand(current.getAttribute("name")); + + if (log.isDebugEnabled()) { + log.debug("dc:element -> '" + attr + "'"); + } + + if (attr.length() == 0) { + log.warn("no name attribute found"); + return; + } + + Element element = output.createElement(attr); + + NodeList children = current.getChildNodes(); + for (int i = 0, N = children.getLength(); i < N; ++i) { + build(element, children.item(i)); + } + + parent.appendChild(element); + } + + protected void text(Node parent, Element current) + throws SQLException + { + log.debug("dc:text"); + String value = expand(current.getTextContent()); + parent.appendChild(output.createTextNode(value)); + } + + + protected void attribute(Node parent, Element current) { + + if (parent.getNodeType() != Node.ELEMENT_NODE) { + log.warn("need element here"); + return; + } + + String name = expand(current.getAttribute("name")); + String value = expand(current.getAttribute("value")); + + Element element = (Element)parent; + + element.setAttribute(name, value); + } + + protected void convert(Node parent, Element current) { + + String variable = expand(current.getAttribute("var")); + String type = expand(current.getAttribute("type")); + + if (frames.containsKey(variable)) { + Object object = TypeConverter.convert( + frames.get(variable), + type); + frames.put(variable, object); + } + } + + protected String expand(String s) { + Matcher m = CompiledStatement.VAR.matcher(s); + + StringBuffer sb = new StringBuffer(); + while (m.find()) { + String key = m.group(1); + Object value = frames.get(key); + m.appendReplacement(sb, value != null ? value.toString() : ""); + } + m.appendTail(sb); + return sb.toString(); + } + + protected void build(Node parent, Node current) + throws SQLException + { + String ns = current.getNamespaceURI(); + if (ns != null && ns.equals(DC_NAMESPACE_URI)) { + if (current.getNodeType() != Node.ELEMENT_NODE) { + log.warn("need elements here"); + } + else { + String localName = current.getLocalName(); + if ("context".equals(localName)) { + context(parent, (Element)current); + } + else if ("attribute".equals(localName)) { + attribute(parent, (Element)current); + } + else if ("element".equals(localName)) { + element(parent, (Element)current); + } + else if ("text".equals(localName)) { + text(parent, (Element)current); + } + else if ("convert".equals(localName)) { + convert(parent, (Element)current); + } + else { + log.warn("unknown '" + localName + "' -> ignore"); + } + } + return; + } + + if (current.getNodeType() == Node.TEXT_NODE) { + String txt = current.getNodeValue(); + if (txt != null && txt.trim().length() == 0) { + return; + } + } + + Node copy = output.importNode(current, false); + + NodeList children = current.getChildNodes(); + for (int i = 0, N = children.getLength(); i < N; ++i) { + build(copy, children.item(i)); + } + parent.appendChild(copy); + } + } // class BuildHelper + + + public Builder() { + } + + public Builder(Document template) { + this.template = template; + } + + public Document build(Connection connection) + throws SQLException + { + return build(connection, XMLUtils.newDocument(), null); + } + + public Document build( + Connection connection, + Document output, + Map<String, Object> parameters + ) + throws SQLException + { + NodeList roots = template.getElementsByTagNameNS( + DC_NAMESPACE_URI, "template"); + + BuildHelper helper = new BuildHelper(output, connection, parameters); + + List<Node> elements = new ArrayList<Node>(); + + for (int i = 0, N = roots.getLength(); i < N; ++i) { + NodeList rootChildren = roots.item(i).getChildNodes(); + for (int j = 0, M = rootChildren.getLength(); j < M; ++j) { + Node child = rootChildren.item(j); + if (child.getNodeType() == Node.ELEMENT_NODE) { + elements.add(child); + } + } + } + helper.build(elements); + + return output; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,100 @@ +package de.intevation.flys.artifacts.services.meta; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Connection; +import java.sql.ResultSet; + +public class CompiledStatement +{ + public static final Pattern VAR = Pattern.compile("\\$\\{([a-zA-Z0-9_]+)\\}"); + + protected String original; + protected String statement; + + protected Map<String, List<Integer>> positions; + + protected PreparedStatement preparedStatement; + + public CompiledStatement() { + } + + public CompiledStatement(String original) { + this.original = original; + positions = new HashMap<String, List<Integer>>(); + compile(); + } + + protected void compile() { + + StringBuffer sb = new StringBuffer(); + + Matcher m = VAR.matcher(original); + + int index = 1; + + while (m.find()) { + String key = m.group(1); + List<Integer> indices = positions.get(key); + if (indices == null) { + indices = new ArrayList<Integer>(); + positions.put(key, indices); + } + indices.add(index); + m.appendReplacement(sb, "?"); + ++index; + } + + m.appendTail(sb); + + statement = sb.toString(); + } + + public String getStatement() { + return statement; + } + + public ResultData execute(Connection connection, StackFrames frames) + throws SQLException + { + if (preparedStatement == null) { + preparedStatement = connection.prepareStatement(statement); + } + + for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) { + Object value = frames.get(entry.getKey()); + for (Integer index: entry.getValue()) { + preparedStatement.setObject(index, value); + } + } + + ResultSet result = preparedStatement.executeQuery(); + try { + return new ResultData(preparedStatement.getMetaData()) + .addAll(result); + } + finally { + result.close(); + } + } + + public void close() { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } + catch (SQLException sqle) { + } + preparedStatement = null; + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,57 @@ +package de.intevation.flys.artifacts.services.meta; + +import java.sql.ResultSetMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; + +import java.util.List; +import java.util.ArrayList; + +public class ResultData +{ + protected String [] columns; + + protected List<Object []> rows; + + public ResultData() { + rows = new ArrayList<Object []>(); + } + + public ResultData(ResultSetMetaData meta) + throws SQLException + { + this(); + + int N = meta.getColumnCount(); + + columns = new String[N]; + + for (int i = 1; i <= N; ++i) { + columns[i-1] = meta.getColumnLabel(i); + } + } + + public String [] getColumnLabels() { + return columns; + } + + public ResultData addAll(ResultSet result) throws SQLException { + while (result.next()) { + add(result); + } + return this; + } + + public void add(ResultSet result) throws SQLException { + Object [] row = new Object[columns.length]; + for (int i = 0; i < columns.length; ++i) { + row[i] = result.getObject(i+1); + } + rows.add(row); + } + + public List<Object []> getRows() { + return rows; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,70 @@ +package de.intevation.flys.artifacts.services.meta; + +import java.util.Map; +import java.util.List; +import java.util.HashMap; +import java.util.ArrayList; + +public class StackFrames +{ + protected List<Map<String, Object>> frames; + + public StackFrames() { + frames = new ArrayList<Map<String, Object>>(); + } + + public StackFrames(Map<String, Object> initialFrame) { + this(); + if (initialFrame != null) { + frames.add(new HashMap<String, Object>(initialFrame)); + } + } + + public void enter() { + frames.add(new HashMap<String, Object>()); + } + + public void leave() { + frames.remove(frames.size()-1); + } + + public void put(String key, Object value) { + int N = frames.size(); + if (N > 0) { + frames.get(N-1).put(key, value); + } + } + + public void put(String [] keys, Object [] values) { + Map<String, Object> top = frames.get(frames.size()-1); + for (int i = 0; i < keys.length; ++i) { + top.put(keys[i], values[i]); + } + } + + public boolean containsKey(String key) { + for (int i = frames.size()-1; i >= 0; --i) { + if (frames.get(i).containsKey(key)) { + return true; + } + } + return false; + } + + public Object get(String key) { + return get(key, null); + } + + public Object get(String key, Object def) { + + for (int i = frames.size()-1; i >= 0; --i) { + Map<String, Object> frame = frames.get(i); + if (frame.containsKey(key)) { + return frame.get(key); + } + } + + return def; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/TypeConverter.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,31 @@ +package de.intevation.flys.artifacts.services.meta; + +public class TypeConverter +{ + private TypeConverter() { + } + + public static Object convert(Object object, String type) { + + if (type == null) { + return object; + } + + if ("Integer".equals(type)) { + return Integer.valueOf(object.toString()); + } + + if ("Double".equals(type)) { + return Double.valueOf(object.toString()); + } + + if ("String".equals(type)) { + return object.toString(); + } + + // TODO: Add more types + + return object; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,133 @@ +package de.intevation.flys.artifacts.states; + +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.artifacts.resources.Resources; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class CalculationSelect extends DefaultState { + + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(CalculationSelect.class); + + + /** Constant value for the reference line calculation.*/ + public static final String CALCULATION_SURFACE_CURVE = + "calc.surface.curve"; + + /** Constant value for the differences calculation.*/ + public static final String CALCULATION_DURATION_CURVE = + "calc.duration.curve"; + + /** Constant value for the flood map calculation.*/ + public static final String CALCULATION_FLOOD_MAP = + "calc.flood.map"; + + /** Constant value for the profile calculation.*/ + public static final String CALCULATION_DISCHARGE_LONGITUDINAL_CURVE = + "calc.discharge.longitudinal.section"; + + /** Constant value for the state discharge curve calculation.*/ + public static final String CALCULATION_DISCHARGE_CURVE = + "calc.discharge.curve"; + + /** An array that holds all available calculation modes.*/ + public static final String[] CALCULATIONS = { + CALCULATION_SURFACE_CURVE, + CALCULATION_FLOOD_MAP, + CALCULATION_DISCHARGE_CURVE, + CALCULATION_DURATION_CURVE, + CALCULATION_DISCHARGE_LONGITUDINAL_CURVE }; + + + /** Error message that is thrown if no mode has been chosen.*/ + public static final String ERROR_NO_CALCULATION_MODE = + "error_feed_no_calculation_mode"; + + /** Error message that is thrown if an invalid calculation mode has been + * chosen.*/ + public static final String ERROR_INVALID_CALCULATION_MODE = + "error_feed_invalid_calculation_mode"; + + + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + CallMeta meta = context.getMeta(); + Element[] calcs = new Element[CALCULATIONS.length]; + + int i = 0; + + for (String calc: CALCULATIONS) { + calcs[i++] = createItem( + cr, new String[] { + Resources.getMsg(meta, calc, calc), + calc + }); + } + + return calcs; + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + @Override + public boolean validate(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("CalculationSelect.validate"); + + Map<String, StateData> data = getData(); + + String calc = (String) data.get("calculation_mode").getValue(); + + if (calc == null) { + throw new IllegalArgumentException(ERROR_NO_CALCULATION_MODE); + } + + calc = calc.trim().toLowerCase(); + + for (String mode: CALCULATIONS) { + if (mode.equals(calc)) { + return true; + } + } + + throw new IllegalArgumentException(ERROR_INVALID_CALCULATION_MODE); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,36 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * The final state that will be reached after the discharge curve calculation + * mode has been chosen. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ComputedDischargeCurveState extends DefaultState { + + /** The logger that is used in this state.*/ + private static Logger logger = + Logger.getLogger(ComputedDischargeCurveState.class); + + + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("ComputedDischargeCurveState.createItems"); + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DefaultState.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,211 @@ +package de.intevation.flys.artifacts.states; + +import java.util.Collection; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; +import de.intevation.artifactdatabase.state.AbstractState; + +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class DefaultState extends AbstractState { + + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(DefaultState.class); + + + public Element describeStatic( + Document document, + Node root, + CallContext context, + String uuid) + { + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + document, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + CallMeta meta = context.getMeta(); + + String label = Resources.getMsg(meta, getID(), getID()); + Element ui = ProtocolUtils.createArtNode( + creator, "state", + new String[] { "name", "uiprovider", "label" }, + new String[] { getID(), getUIProvider(), label }); + + Map<String, StateData> theData = getData(); + if (theData == null) { + return ui; + } + + Collection<StateData> dataItems = theData.values(); + + for (StateData data: dataItems) { + String name = data.getName(); + String value = (String) data.getValue(); + + if (value == null) { + continue; + } + + logger.debug("Append element '" + name + "' (" + value + ")"); + + Element dataElement = creator.create("data"); + creator.addAttr(dataElement, "name", name, true); + creator.addAttr(dataElement, "type", data.getType(), true); + + Element itemElement = creator.create("item"); + creator.addAttr(itemElement, "value", value, true); + creator.addAttr( + itemElement, + "label", + Resources.getMsg(meta, value, value), + true); + + dataElement.appendChild(itemElement); + ui.appendChild(dataElement); + } + + return ui; + } + + + public Element describe( + Artifact artifact, + Document document, + Node root, + CallContext context, + String uuid) + { + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + document, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element ui = null; + String uiprovider = getUIProvider(); + if (uiprovider != null) { + ui = ProtocolUtils.createArtNode( + creator, "dynamic", + new String[] { "uiprovider" }, + new String[] { uiprovider }); + } + else { + ui = ProtocolUtils.createArtNode(creator, "dynamic", null, null); + } + + Map<String, StateData> theData = getData(); + if (theData == null) { + return ui; + } + + Collection<StateData> dataItems = theData.values(); + + for (StateData data: dataItems) { + String name = data.getName(); + Element select = createData(creator, artifact, data, context); + + Element choices = ProtocolUtils.createArtNode( + creator, "choices", null, null); + + select.appendChild(choices); + ui.appendChild(select); + + Element[] items = createItems(creator, artifact, name, context); + for (Element item: items) { + choices.appendChild(item); + } + } + + return ui; + } + + + /** + * This method creates the root node that contains the list of selectable + * items. + * + * @param cr The ElementCreator. + * @param name The name of the amount of data. + * + * @return the root node of the item list. + */ + protected Element createData( + XMLUtils.ElementCreator cr, + Artifact artifact, + StateData data, + CallContext context) + { + Element select = ProtocolUtils.createArtNode( + cr, "select", null, null); + cr.addAttr(select, "name", data.getName(), true); + + Element label = ProtocolUtils.createArtNode( + cr, "label", null, null); + + select.appendChild(label); + + label.setTextContent(Resources.getMsg( + context.getMeta(), + getID(), + getID())); + + return select; + } + + + /** + * This method creates a list of items. These items represent the amount of + * input data that is possible for this state. + * + * @param cr The ElementCreator. + * @param name The name of the amount of data. + * + * @return a list of items. + */ + protected abstract Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context); + + + /** + * This method validates the inserted data and returns true, if everything + * was correct, otherwise an exception is thrown. + * + * @param artifact A reference to the owner artifact. + * @param context The CallContext object. + * + * @return true, if everything was fine. + */ + public boolean validate(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + return true; + } + + + protected String getUIProvider() { + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,30 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +public class DischargeLongitudinalSection extends DefaultState { + + /** The logger that is used in this state.*/ + private static Logger logger = + Logger.getLogger(DischargeLongitudinalSection.class); + + + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("DischargeLongitudinalSection.createItems"); + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,35 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * The final state that will be reached after the duration curve calculation + * mode has been chosen. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DurationCurveState extends DefaultState { + + /** The logger that is used in this state.*/ + private static Logger logger = Logger.getLogger(DurationCurveState.class); + + + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("DurationCurveState.createItems"); + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,185 @@ +package de.intevation.flys.artifacts.states; + +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.RiverFactory; +import de.intevation.flys.artifacts.resources.Resources; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class LocationDistanceSelect extends RangeState { + + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(LocationDistanceSelect.class); + + + /** The default step width.*/ + public static final String DEFAULT_STEP = "100"; + + /** The name of the 'from' field. */ + public static final String FROM = "ld_from"; + + /** The name of the 'to' field. */ + public static final String TO = "ld_to"; + + /** The name of the 'step' field. */ + public static final String STEP = "ld_step"; + + /** + * The default constructor that initializes an empty State object. + */ + public LocationDistanceSelect() { + } + + protected Element createData( + XMLUtils.ElementCreator cr, + Artifact artifact, + StateData data, + CallContext context) + { + Element select = ProtocolUtils.createArtNode( + cr, "select", null, null); + + cr.addAttr(select, "name", data.getName(), true); + + Element label = ProtocolUtils.createArtNode( + cr, "label", null, null); + + Element choices = ProtocolUtils.createArtNode( + cr, "choices", null, null); + + label.setTextContent(Resources.getMsg( + context.getMeta(), + data.getName(), + data.getName())); + + select.appendChild(label); + + return select; + } + + + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + double[] minmax = getMinMaxDistance(artifact); + + double minVal = Double.MIN_VALUE; + double maxVal = Double.MAX_VALUE; + + if (minmax != null) { + minVal = minmax[0]; + maxVal = minmax[1]; + } + else { + logger.warn("Could not read min/max distance values!"); + } + + if (name.equals("ld_from")) { + Element min = createItem( + cr, + new String[] {"min", new Double(minVal).toString()}); + + return new Element[] { min }; + } + else if (name.equals("ld_to")) { + Element max = createItem( + cr, + new String[] {"max", new Double(maxVal).toString()}); + + return new Element[] { max }; + } + else { + Element step = createItem(cr, new String[] {"step", DEFAULT_STEP}); + return new Element[] { step }; + } + + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + protected String getUIProvider() { + return "location_distance_panel"; + } + + + protected double[] getMinMaxDistance(Artifact artifact) { + FLYSArtifact flysArtifact = (FLYSArtifact) artifact; + StateData data = flysArtifact.getData("river"); + + String name = (String) data.getValue(); + + logger.debug("Search for the min/max distances of '" + name + "'"); + + River river = RiverFactory.getRiver(name); + + return river != null ? river.determineMinMaxDistance() : null; + } + + + @Override + public boolean validate(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("LocationDistanceSelect.validate"); + + Map<String, StateData> data = getData(); + + String fromStr = (String) data.get(FROM).getValue(); + String toStr = (String) data.get(TO).getValue(); + String stepStr = (String) data.get(STEP).getValue(); + + if (fromStr == null || toStr == null || stepStr == null) { + throw new IllegalArgumentException("error_empty_state"); + } + + try { + double from = Double.parseDouble(fromStr); + double to = Double.parseDouble(toStr); + double step = Double.parseDouble(stepStr); + + double[] minmaxDist = getMinMaxDistance(artifact); + + return validateBounds(minmaxDist[0], minmaxDist[1], from, to, step); + } + catch (NumberFormatException nfe) { + throw new IllegalArgumentException("error_feed_number_format"); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,93 @@ +package de.intevation.flys.artifacts.states; + +import java.util.Map; + +import gnu.trove.TDoubleArrayList; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.data.StateData; + + +/** + * This state is used to realize the input of multiple locations as string. The + * string should be a whitespace separated list of double values where each + * double value represents a location. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class LocationSelect extends LocationDistanceSelect { + + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(LocationSelect.class); + + + /** The name of the StateData object that stores the location string.*/ + public static final String FIELD_LOCATIONS = "ld_locations"; + + + public LocationSelect() { + } + + + @Override + protected String getUIProvider() { + return "location_panel"; + } + + + @Override + public boolean validate(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("LocationSelect.validate"); + + Map<String, StateData> data = getData(); + + String locationStr = (String) data.get(FIELD_LOCATIONS).getValue(); + + if (locationStr == null || locationStr.length() == 0) { + logger.error("No locations given."); + throw new IllegalArgumentException("error_empty_state"); + } + + double[] minmax = getMinMaxDistance(artifact); + double[] mm = extractLocations(locationStr); + + logger.debug("Inserted min location: " + mm[0]); + logger.debug("Inserted max location: " + mm[mm.length-1]); + + return validateBounds(minmax[0], minmax[1], mm[0], mm[mm.length-1], 0d); + } + + + /** + * This method takes a string that consist of whitespace separated double + * values and returns the double values as array. + * + * @param locationStr The locations inserted in this state. + * + * @return the locations as array. + */ + protected double[] extractLocations(String locationStr) { + String[] tmp = locationStr.split(" "); + TDoubleArrayList locations = new TDoubleArrayList(); + + for (String l: tmp) { + try { + locations.add(Double.parseDouble(l)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + locations.sort(); + + return locations.toNativeArray(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RangeState.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,54 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class RangeState extends DefaultState { + + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(RangeState.class); + + + public RangeState() { + } + + + /** + * Validates a given range with a given valid range. + * + * @param fromValid Valid lower value of the range. + * @param toValid Valid upper value of the range. + * @param from The lower value. + * @param to The upper value. + * @param step The step width. + * + * @return true, if everything was fine, otherwise an exception is thrown. + */ + protected boolean validateBounds( + double fromValid, double toValid, + double from, double to, double step) + throws IllegalArgumentException + { + logger.debug("RangeState.validateRange"); + + if (from < fromValid) { + logger.error( + "Invalid 'from'. " + from + " is smaller than " + fromValid); + throw new IllegalArgumentException("error_feed_from_out_of_range"); + } + else if (to > toValid) { + logger.error( + "Invalid 'to'. " + to + " is bigger than " + toValid); + throw new IllegalArgumentException("error_feed_to_out_of_range"); + } + else if (from > to) { + throw new IllegalArgumentException("error_feed_from_bigger_to"); + } + + return true; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,162 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.model.RiverFactory; +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class RiverSelect extends DefaultState { + + /** The logger used in this class. */ + private static Logger logger = Logger.getLogger(RiverSelect.class); + + + /** Error message that is thrown if no river was found based on a given + * name.*/ + public static final String ERROR_NO_SUCH_RIVER = + "error_feed_no_such_river"; + + /** Error message that is thrown if no river was found based on a given + * name.*/ + public static final String ERROR_NO_RIVER_SELECTED = + "error_feed_no_river_selected"; + + + /** + * The default constructor that initializes an empty State object. + */ + public RiverSelect() { + } + + + /** + * Initialize the state based on the state node in the configuration. + * + * @param config The state configuration node. + */ + public void setup(Node config) { + super.setup(config); + } + + + protected Element createData( + XMLUtils.ElementCreator cr, + Artifact artifact, + StateData data, + CallContext context) + { + Element select = ProtocolUtils.createArtNode( + cr, "select", + new String[] { "uiprovider" }, + new String[] { "select_with_map" }); + cr.addAttr(select, "name", data.getName(), true); + + Element label = ProtocolUtils.createArtNode( + cr, "label", null, null); + + Element choices = ProtocolUtils.createArtNode( + cr, "choices", null, null); + + select.appendChild(label); + + label.setTextContent(Resources.getMsg( + context.getMeta(), + getID(), + getID())); + + return select; + } + + + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + List<River> rivers = RiverFactory.getRivers(); + Element[] items = new Element[rivers.size()]; + + int idx = 0; + for (River river: rivers) { + items[idx++] = createRiverItem(cr, river); + } + + return items; + } + + + /** + * This method creates a node that represents a river item. This node + * contains the label and the value that describe the river. + * + * @param cr The ElementCreator. + * @param river The river. + * + * @return the element that contains the information about the river. + */ + protected Element createRiverItem(XMLUtils.ElementCreator cr, River river) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + label.setTextContent(river.getName()); + value.setTextContent(river.getName()); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + @Override + public boolean validate(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("RiverSelect.validate"); + + Map<String, StateData> data = getData(); + + StateData dRiver = data.get("river"); + + if (dRiver == null || dRiver.getValue() == null) { + throw new IllegalArgumentException(ERROR_NO_RIVER_SELECTED); + } + + River river = RiverFactory.getRiver((String) dRiver.getValue()); + + if (river == null) { + throw new IllegalArgumentException(ERROR_NO_SUCH_RIVER); + } + + return true; + } + + + @Override + protected String getUIProvider() { + return "river_panel"; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StateFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,121 @@ +package de.intevation.flys.artifacts.states; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class StateFactory { + + /** The logger used in this class */ + private static Logger logger = Logger.getLogger(StateFactory.class); + + /** The XPath to the classname of the state */ + public static final String XPATH_STATE = "@state"; + + /** The XPath to the data items of the state relative to the state node. */ + public static final String XPATH_DATA = "data"; + + /** The XPath to the data name relative to the data node.*/ + public static final String XPATH_DATA_NAME = "@name"; + + /** The XPath to the data type relative to the data node.*/ + public static final String XPATH_DATA_TYPE = "@type"; + + /** The XPath to the data description relative to the data node.*/ + public static final String XPATH_DATA_DESCRIPTION = "@description"; + + + /** + * Creates a new State based on the configured class provided by + * <code>stateConf</code>. + * + * @param stateConf The configuration of the state. + * + * @return a State. + */ + public static State createState(Node stateConf) { + String clazz = (String) XMLUtils.xpath( + stateConf, XPATH_STATE, XPathConstants.STRING); + + State state = null; + + try { + logger.debug("Create a new State for class: " + clazz); + state = (State) Class.forName(clazz).newInstance(); + state.setup(stateConf); + + initializeStateData(state, stateConf); + } + catch (InstantiationException ie) { + logger.error(ie, ie); + } + catch (IllegalAccessException iae) { + logger.error(iae, iae); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe, cnfe); + } + + return state; + } + + + /** + * This method extracts the configured input data of a state and adds new + * StateData objects to the State. + * + * @param state The state. + * @param stateConf The state configuration node. + */ + protected static void initializeStateData(State state, Node stateConf) { + NodeList dataList = (NodeList) XMLUtils.xpath( + stateConf, XPATH_DATA, XPathConstants.NODESET); + + if (dataList == null || dataList.getLength() == 0) { + logger.debug("The state has no input data configured."); + + return; + } + + int items = dataList.getLength(); + + logger.debug("The state has " + items + " data items configured."); + + for (int i = 0; i < items; i++) { + Node data = dataList.item(i); + + String name = (String) XMLUtils.xpath( + data, XPATH_DATA_NAME, XPathConstants.STRING); + String type = (String) XMLUtils.xpath( + data, XPATH_DATA_TYPE, XPathConstants.STRING); + String desc = (String) XMLUtils.xpath( + data, XPATH_DATA_DESCRIPTION, XPathConstants.STRING); + + if (name == null || name.equals("")) { + logger.warn("No name for data item at pos " + i + " found."); + continue; + } + + if (type == null || type.equals("")) { + logger.warn("No type for data item at pos " + i + " found."); + logger.warn("Default type 'string' used."); + type = "string"; + } + + state.addData(name, new DefaultStateData(name, type, desc)); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,321 @@ +package de.intevation.flys.artifacts.states; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.Range; +import de.intevation.flys.model.River; +import de.intevation.flys.model.Wst; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.RangeWithValues; +import de.intevation.flys.artifacts.model.WstFactory; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQAdapted extends DefaultState { + + /** The logger used in this state.*/ + private static Logger logger = Logger.getLogger(WQAdapted.class); + + + public static final String FIELD_WQ_MODE = "wq_mode"; + + public static final String FIELD_WQ_VALUES = "wq_values"; + + + /** + * This method creates one element for each gauge of the selected river that + * is intersected by the given kilometer range. Each element is a tuple of + * (from;to) where <i>from</i> is the lower bounds of the gauge or the lower + * kilometer range. <i>to</i> is the upper bounds of the gauge or the upper + * kilometer range. + * + * @param cr The ElementCreator. + * @param artifact The FLYS artifact. + * @param name The name of the data item. + * @param context The CallContext. + * + * @return a list of elements that consist of tuples of the intersected + * gauges of the selected river. + */ + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("WQAdapted.createItems"); + + if (name != null && name.equals(FIELD_WQ_MODE)) { + return createModeItems(cr, artifact, name, context); + } + else if (name != null && name.equals(FIELD_WQ_VALUES)) { + return createValueItems(cr, artifact, name, context); + } + else { + logger.warn("Unknown data object: " + name); + return null; + } + } + + + protected Element[] createModeItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("WQAdapted.createModeItems"); + + Element w = createItem(cr, new String[] { "w", "W" }); + Element q = createItem(cr, new String[] { "q", "Q" }); + + return new Element[] { w, q }; + } + + + protected Element[] createValueItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("WQAdapted.createValueItems"); + + FLYSArtifact flysArtifact = (FLYSArtifact) artifact; + + double[] dist = flysArtifact.getDistance(); + List<Gauge> gauges = flysArtifact.getGauges(); + + int num = gauges != null ? gauges.size() : 0; + + if (num == 0) { + logger.warn("Selected distance matches no gauges."); + return null; + } + + Element[] elements = new Element[num]; + + int idx = 0; + + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + double lower = range.getA().doubleValue(); + double upper = range.getB().doubleValue(); + + double from = dist[0] < lower ? lower : dist[0]; + double to = dist[1] > upper ? upper : dist[1]; + + String key = Double.toString(from) + ";" + Double.toString(to); + + elements[idx++] = createItem(cr, new String[] {key, ""}); + } + + return elements; + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + @Override + protected String getUIProvider() { + return "wq_panel_adapted"; + } + + + public boolean validate(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("WQAdapted.validate"); + + Map<String, StateData> data = getData(); + + String mode = (String) data.get(FIELD_WQ_MODE).getValue(); + + if (mode != null && mode.equals("W")) { + return validateW(artifact, context); + } + else if (mode != null && mode.equals("Q")) { + return validateQ(artifact, context); + } + else { + throw new IllegalArgumentException("error_feed_no_wq_mode_selected"); + } + } + + + protected boolean validateW(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("WQAdapted.validateW"); + + RangeWithValues[] rwvs = extractInput(getData("wq_values")); + List<Gauge> gauges = ((FLYSArtifact) artifact).getGauges(); + + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + double lower = range.getA().doubleValue(); + double upper = range.getB().doubleValue(); + + for (RangeWithValues rwv: rwvs) { + if (lower <= rwv.getLower() && upper >= rwv.getUpper()) { + compareWsWithGauge(gauge, rwv.getValues()); + } + } + } + + return true; + } + + + protected boolean validateQ(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("WQAdapted.validateQ"); + + RangeWithValues[] rwvs = extractInput(getData("wq_values")); + List<Gauge> gauges = ((FLYSArtifact) artifact).getGauges(); + River river = ((FLYSArtifact) artifact).getRiver(); + Wst wst = WstFactory.getWst(river); + + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + double lower = range.getA().doubleValue(); + double upper = range.getB().doubleValue(); + + for (RangeWithValues rwv: rwvs) { + if (lower <= rwv.getLower() && upper >= rwv.getUpper()) { + compareQsWithGauge(wst, gauge, rwv.getValues()); + } + } + } + + return true; + } + + + protected boolean compareQsWithGauge(Wst wst, Gauge gauge, double[] qs) + throws IllegalArgumentException + { + double[] minmax = gauge != null + ? wst.determineMinMaxQ(gauge.getRange()) + : null; + + if (minmax == null) { + logger.warn("Could not determine min/max Q of gauge."); + return true; + } + + if (logger.isDebugEnabled()) { + logger.debug("Validate Qs with:"); + logger.debug("-- Gauge: " + gauge.getName()); + logger.debug("-- Gauge min: " + minmax[0]); + logger.debug("-- Gauge max: " + minmax[1]); + } + + for (double q: qs) { + if (q < minmax[0] || q > minmax[1]) { + throw new IllegalArgumentException( + "error_feed_q_values_invalid"); + } + } + + return true; + } + + + protected boolean compareWsWithGauge(Gauge gauge, double[] ws) + throws IllegalArgumentException + { + double[] minmax = gauge != null + ? gauge.determineMinMaxW() + : null; + + if (minmax == null) { + logger.warn("Could not determine min/max W of gauge."); + return true; + } + + if (logger.isDebugEnabled()) { + logger.debug("Validate Ws with:"); + logger.debug("-- Gauge: " + gauge.getName()); + logger.debug("-- Gauge min: " + minmax[0]); + logger.debug("-- Gauge max: " + minmax[1]); + } + + for (double w: ws) { + if (w < minmax[0] || w > minmax[1]) { + throw new IllegalArgumentException( + "error_feed_w_values_invalid"); + } + } + + return true; + } + + + protected RangeWithValues[] extractInput(StateData data) { + String dataString = (String) data.getValue(); + String[] ranges = dataString.split(":"); + + List<RangeWithValues> rwv = new ArrayList<RangeWithValues>(); + + for (String range: ranges) { + String[] parts = range.split(";"); + + double lower = Double.parseDouble(parts[0]); + double upper = Double.parseDouble(parts[1]); + + String[] values = parts[2].split(","); + + int num = values.length; + double[] res = new double[num]; + + for (int i = 0; i < num; i++) { + try { + res[i] = Double.parseDouble(values[i]); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + rwv.add(new RangeWithValues(lower, upper, res)); + } + + return (RangeWithValues[]) rwv.toArray(new RangeWithValues[rwv.size()]); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQSelect.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,356 @@ +package de.intevation.flys.artifacts.states; + +import java.util.Map; + +import gnu.trove.TDoubleArrayList; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; +import de.intevation.flys.model.Wst; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.WstFactory; +import de.intevation.flys.artifacts.resources.Resources; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQSelect extends RangeState { + + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(WQSelect.class); + + + /** The default step width for Qs.*/ + public static final String DEFAULT_STEP_Q = "50"; + + /** The default step width for Qs.*/ + public static final String DEFAULT_STEP_W = "30"; + + /** The name of the 'mode' field. */ + public static final String WQ_MODE = "wq_mode"; + + /** The name of the 'selection' field.*/ + public static final String WQ_SELECTION = "wq_selection"; + + /** The name of the 'from' field. */ + public static final String WQ_FROM = "wq_from"; + + /** The name of the 'to' field. */ + public static final String WQ_TO = "wq_to"; + + /** The name of the 'step' field. */ + public static final String WQ_STEP = "wq_step"; + + /** The name of the 'single' field. */ + public static final String WQ_SINGLE = "wq_single"; + + + /** + * The default constructor that initializes an empty State object. + */ + public WQSelect() { + } + + protected Element createData( + XMLUtils.ElementCreator cr, + Artifact artifact, + StateData data, + CallContext context) + { + Element select = ProtocolUtils.createArtNode( + cr, "select", null, null); + + cr.addAttr(select, "name", data.getName(), true); + + Element label = ProtocolUtils.createArtNode( + cr, "label", null, null); + + Element choices = ProtocolUtils.createArtNode( + cr, "choices", null, null); + + label.setTextContent(Resources.getMsg( + context.getMeta(), + data.getName(), + data.getName())); + + select.appendChild(label); + + return select; + } + + + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + // TODO Insert correct min/max values! + double[] minmaxW = determineMinMaxW(artifact); + double[] minmaxQ = determineMinMaxQ(artifact); + + if (name.equals("wq_from")) { + Element minW = createItem( + cr, new String[] {"minW", new Double(minmaxW[0]).toString()}); + Element minQ = createItem( + cr, new String[] {"minQ", new Double(minmaxQ[0]).toString()}); + return new Element[] { minW, minQ }; + } + else if (name.equals("wq_to")) { + Element maxW = createItem( + cr, new String[] {"maxW", new Double(minmaxW[1]).toString()}); + Element maxQ = createItem( + cr, new String[] {"maxQ", new Double(minmaxQ[1]).toString()}); + return new Element[] { maxW, maxQ }; + } + else { + Element stepW = createItem(cr, new String[] {"stepW", DEFAULT_STEP_W}); + Element stepQ = createItem(cr, new String[] {"stepQ", DEFAULT_STEP_Q}); + return new Element[] { stepW, stepQ }; + } + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + protected String getUIProvider() { + return "wq_panel"; + } + + + /** + * Determines the min and max W value for the current gauge. If no min and + * max values could be determined, this method will return + * [Double.MIN_VALUE, Double.MAX_VALUE]. + * + * @param artifact The FLYSArtifact. + * + * @return the min and max W values for the current gauge. + */ + protected double[] determineMinMaxW(Artifact artifact) { + logger.debug("WQSelect.determineCurrentGauge"); + + Gauge gauge = ((FLYSArtifact) artifact).getGauge(); + double[] minmaxW = gauge != null ? gauge.determineMinMaxW() : null; + + double minW = minmaxW != null ? minmaxW[0] : Double.MIN_VALUE; + double maxW = minmaxW != null ? minmaxW[1] : Double.MAX_VALUE; + + return new double[] { minW, maxW }; + } + + + /** + * Determines the min and max Q value for the current gauge. If no min and + * max values could be determined, this method will return + * [Double.MIN_VALUE, Double.MAX_VALUE]. + * + * @param artifact The FLYSArtifact. + * + * @return the min and max Q values for the current gauge. + */ + protected double[] determineMinMaxQ(Artifact artifact) { + logger.debug("WQSelect.determineMinMaxQ"); + + FLYSArtifact flysArtifact = (FLYSArtifact) artifact; + + River river = flysArtifact.getRiver(); + Gauge gauge = flysArtifact.getGauge(); + Wst wst = WstFactory.getWst(river); + + double[] minmaxQ = gauge != null + ? wst.determineMinMaxQ(gauge.getRange()) + : null; + + double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE; + double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE; + + return new double[] { minQ, maxQ }; + } + + + @Override + public boolean validate(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("WQSelect.validate"); + + Map<String, StateData> data = getData(); + + String selectionMode = (String) data.get(WQ_SELECTION).getValue(); + + if (selectionMode == null || selectionMode.equals("single")) { + return validateSingle(artifact, context); + } + else { + return validateRange(artifact, context); + } + } + + + protected boolean validateSingle(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("WQSelect.validateSingle"); + + String tmp = (String) data.get(WQ_SINGLE).getValue(); + + if (tmp == null || tmp.length() == 0) { + throw new IllegalArgumentException("error_empty_state"); + } + + String[] strValues = tmp.split(" "); + TDoubleArrayList all = new TDoubleArrayList(); + + for (String strValue: strValues) { + try { + all.add(Double.parseDouble(strValue)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + all.sort(); + + String mode = (String) data.get(WQ_MODE).getValue(); + logger.debug("WQ Mode: " + mode); + + double[] minmax = null; + + if (mode != null && mode.trim().toLowerCase().equals("w")) { + minmax = determineMinMaxW(artifact); + } + else { + minmax = determineMinMaxQ(artifact); + } + + double min = all.get(0); + double max = all.get(all.size()-1); + + logger.debug("Inserted min value = " + min); + logger.debug("Inserted max value = " + max); + + return validateBounds(minmax[0], minmax[1], min, max, 0d); + } + + + protected boolean validateRange(Artifact artifact, CallContext context) + throws IllegalArgumentException + { + logger.debug("WQSelect.validateRange"); + + String mode = (String) data.get(WQ_MODE).getValue(); + logger.debug("WQ Mode: " + mode); + + String fromStr = (String) data.get(WQ_FROM).getValue(); + String toStr = (String) data.get(WQ_TO).getValue(); + String stepStr = (String) data.get(WQ_STEP).getValue(); + + if (fromStr == null || toStr == null || stepStr == null) { + throw new IllegalArgumentException("error_empty_state"); + } + + double from = Double.parseDouble(fromStr); + double to = Double.parseDouble(toStr); + double step = Double.parseDouble(stepStr); + + try { + if (mode != null && mode.trim().toLowerCase().equals("w")) { + return validateW(artifact, context, from, to, step); + } + else if (mode != null && mode.trim().toLowerCase().equals("q")) { + return validateQ(artifact, context, from, to, step); + } + else { + throw new IllegalArgumentException("error_feed_invalid_wq_mode"); + } + } + catch (NumberFormatException nfe) { + throw new IllegalArgumentException("error_feed_number_format"); + } + } + + + /** + * Validates the inserted W values. + * + * @param artifact The owner artifact. + * @param context The CallContext + * @param from The lower value of the W range. + * @param to The upper value of the W range. + * @param step The step width. + * + * @return true, if everything was fine, otherwise an exception is thrown. + */ + protected boolean validateW( + Artifact artifact, + CallContext context, + double from, + double to, + double step) + throws IllegalArgumentException + { + logger.debug("WQSelect.validateW"); + + double[] minmaxW = determineMinMaxW(artifact); + + return validateBounds(minmaxW[0], minmaxW[1], from, to, step); + } + + + /** + * Validates the inserted Q values. + * + * @param artifact The owner artifact. + * @param context The CallContext + * @param from The lower value of the Q range. + * @param to The upper value of the Q range. + * @param step The step width. + * + * @return true, if everything was fine, otherwise an exception is thrown. + */ + protected boolean validateQ( + Artifact artifact, + CallContext context, + double from, + double to, + double step) + throws IllegalArgumentException + { + logger.debug("WQSelect.validateQ"); + + double[] minmaxQ = determineMinMaxQ(artifact); + + return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,104 @@ +package de.intevation.flys.artifacts.transitions; + +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.transition.Transition; + + +/** + * The default implementation of a <code>Transition</code>. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultTransition implements Transition { + + /** The ID of the current state */ + protected String from; + + /** The ID of the target state */ + protected String to; + + + /** + * The default constructor. + */ + public DefaultTransition() { + } + + + /** + * The default constructor. + * + * @param from The current state. + * @param to The target state. + */ + public DefaultTransition(String from, String to) { + this.from = from; + this.to = to; + } + + + public void init(Node config) { + // nothing to do in the default transition + } + + + /** + * Returns the current state ID. + * + * @return the current state ID. + */ + public String getFrom() { + return from; + } + + + /** + * Returns the target state ID. + * + * @return the target state ID. + */ + public String getTo() { + return to; + } + + + /** + * Set the current state ID. + * + * @param to the current state ID. + */ + public void setFrom(String from) { + this.from = from; + } + + + /** + * Set the target state ID. + * + * @param to the target state ID. + */ + public void setTo(String to) { + this.to = to; + } + + + /** + * Determines if its valid to step from state <i>a</i> of an artifact + * <i>artifact</i> to state <i>b</i>. This method always returns true - no + * validation takes place. + * + * @param artifact The owner artifact of state a and b. + * @param a The current state. + * @param b The target state. + * + * @return true, if it is valid to step from a to b, otherwise false. + */ + public boolean isValid(Artifact artifact, State a, State b) { + return true; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,71 @@ +package de.intevation.flys.artifacts.transitions; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Node; + +import de.intevation.artifactdatabase.transition.Transition; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class TransitionFactory { + + /** The logger used in this class */ + private static Logger logger = Logger.getLogger(TransitionFactory.class); + + /** The XPath to the classname of the transition */ + public static final String XPATH_TRANSITION = "@transition"; + + /** The XPath to the current state ID */ + public static final String XPATH_CURRENT_STATE = "from/@state"; + + /** The XPath to the target state ID */ + public static final String XPATH_TARGET_STATE = "to/@state"; + + + /** + * Creates a new Transition based on the configured class provided by + * <code>transitionConf</code>. + * + * @param transitionConf The configuration of the transition. + * + * @return a Transition. + */ + public static Transition createTransition(Node transitionConf) { + String clazz = (String) XMLUtils.xpath( + transitionConf, XPATH_TRANSITION, XPathConstants.STRING); + + Transition transition = null; + + try { + transition = (Transition) Class.forName(clazz).newInstance(); + + String from = (String) XMLUtils.xpath( + transitionConf, XPATH_CURRENT_STATE, XPathConstants.STRING); + String to = (String) XMLUtils.xpath( + transitionConf, XPATH_TARGET_STATE, XPathConstants.STRING); + + transition.init(transitionConf); + transition.setFrom(from); + transition.setTo(to); + } + catch (InstantiationException ie) { + logger.error(ie, ie); + } + catch (IllegalAccessException iae) { + logger.error(iae, iae); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe, cnfe); + } + + return transition; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/ValueCompareTransition.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,118 @@ +package de.intevation.flys.artifacts.transitions; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.data.StateData; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.FLYSArtifact; + + +/** + * This transition compares data objects with a <i>equal</i> or <i>notequal</i> + * operator. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ValueCompareTransition extends DefaultTransition { + + /** The logger that is used in this transition.*/ + private static Logger log = Logger.getLogger(ValueCompareTransition.class); + + + /** XPath that points to the condition's operator.*/ + public static final String XPATH_OPERATOR = "condition/@operator"; + + /** XPath that points to the condition's value.*/ + public static final String XPATH_VALUE = "condition/@value"; + + /** XPath that points to the condition's dataname.*/ + public static final String XPATH_DATANAME = "condition/@data"; + + /** The value of the 'equal' operator.*/ + public static final String OPERATOR_EQUAL = "equal"; + + /** The value of the 'not equal' operator.*/ + public static final String OPERATOR_NOTEQUAL = "notequal"; + + + /** The operator.*/ + protected String operator; + + /** The value used for comparison.*/ + protected String value; + + /** The name of the data used for the comparison.*/ + protected String dataname; + + + + public ValueCompareTransition() { + } + + + public ValueCompareTransition(String from, String to) { + super(from, to); + } + + + @Override + public void init(Node config) { + log.debug("ValueCompareTransition.init"); + + String tmp = (String) XMLUtils.xpath( + config, + XPATH_OPERATOR, + XPathConstants.STRING); + operator = tmp.trim().toLowerCase(); + + value = (String) XMLUtils.xpath( + config, + XPATH_VALUE, + XPathConstants.STRING); + + dataname = (String) XMLUtils.xpath( + config, + XPATH_DATANAME, + XPathConstants.STRING); + } + + + @Override + public boolean isValid(Artifact artifact, State a, State b) { + log.debug("ValueCompareTransition.isValid"); + + FLYSArtifact flysArtifact = (FLYSArtifact) artifact; + + StateData dataObj = flysArtifact.getData(dataname); + String dataVal = dataObj != null ? (String) dataObj.getValue() : ""; + + if (log.isDebugEnabled()) { + log.debug("Compare settings:"); + log.debug("-- dataname: " + dataname); + log.debug("-- operator: " + operator); + log.debug("-- compare value: " + value); + log.debug("-- state value: " + dataVal); + } + + if (operator.equals(OPERATOR_EQUAL)) { + return value.equals(dataVal); + } + else if (operator.equals(OPERATOR_NOTEQUAL)) { + return !(value.equals(dataVal)); + } + + log.error("Wrong operator set! No comparison takes place."); + + return false; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeParser.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,126 @@ +package de.intevation.flys.collections; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.ArtifactNamespaceContext; + +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.Output; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.model.ManagedFacet; + + +public class AttributeParser { + + /** Constant XPath that points to the outputmodes of an artifact.*/ + public static final String XPATH_ARTIFACT_OUTPUTMODES = + "/art:output"; + + + private static Logger logger = Logger.getLogger(AttributeParser.class); + + + protected Map<String, Output> outs; + + + public AttributeParser() { + this.outs = new HashMap<String, Output>(); + } + + + public void parse(Document doc) { + logger.debug("AttributeParser.parse"); + + NodeList outs = (NodeList) XMLUtils.xpath( + doc, + XPATH_ARTIFACT_OUTPUTMODES, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = outs != null ? outs.getLength() : 0; + + logger.debug("Attribute has " + num + " outputs."); + + for (int i = 0; i < num; i++) { + Node out = outs.item(i); + + parseOutput(out); + } + } + + + public Map<String, Output> getOuts() { + return outs; + } + + + protected void addItem(String out, ManagedFacet item) { + Output o = outs.get(out); + + if (o != null) { + o.addFacet(item); + } + } + + + protected void parseOutput(Node out) { + String name = XMLUtils.xpathString( + out, "@name", ArtifactNamespaceContext.INSTANCE); + + if (outs.get(name) == null) { + logger.debug("Create new output: " + name); + + Output o = new DefaultOutput(name, null, null); + outs.put(name, o); + } + + parseItems(out, name); + } + + + protected void parseItems(Node out, String outname) { + NodeList themes = (NodeList) XMLUtils.xpath( + out, "art:theme", + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = themes != null ? themes.getLength() : 0; + + logger.debug("Output has " + num + " themes."); + + for (int i = 0; i < num; i++) { + Node theme = themes.item(i); + + String name = XMLUtils.xpathString( + theme, "@facet", ArtifactNamespaceContext.INSTANCE); + + String uuid = XMLUtils.xpathString( + theme, "@artifact", ArtifactNamespaceContext.INSTANCE); + + String pos = XMLUtils.xpathString( + theme, "@pos", ArtifactNamespaceContext.INSTANCE); + + String active = XMLUtils.xpathString( + theme, "@active", ArtifactNamespaceContext.INSTANCE); + + ManagedFacet item = new ManagedFacet( + name, "", uuid, + Integer.parseInt(pos), + Integer.parseInt(active)); + + addItem(outname, item); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeWriter.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,148 @@ +package de.intevation.flys.collections; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.ArtifactNamespaceContext; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.flys.artifacts.model.ManagedFacet; + + +public class AttributeWriter { + + protected Map<String, Output> oldAttr; + protected Map<String, Output> newAttr; + + private static Logger logger = Logger.getLogger(AttributeWriter.class); + + + public AttributeWriter( + Map<String, Output> oldAttr, + Map<String, Output> newAttr) + { + this.oldAttr = oldAttr; + this.newAttr = newAttr; + } + + + protected Document write() { + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element outs = cr.create("outputs"); + doc.appendChild(outs); + + Iterator<String> iter = newAttr.keySet().iterator(); + + while (iter.hasNext()) { + String outName = iter.next(); + + Output a = newAttr.get(outName); + Output b = oldAttr.get(outName); + + writeOutput(doc, outs, cr, a, b); + } + + return doc; + } + + + protected void writeOutput( + Document doc, + Node outs, + ElementCreator cr, + Output a, + Output b) + { + Element output = cr.create("output"); + cr.addAttr(output, "name", a.getName()); + + outs.appendChild(output); + + List<Facet> facetsA = a.getFacets(); + List<Facet> facetsB = null; + + if (b != null) { + facetsB = b.getFacets(); + } + + writeFacets(doc, cr, output, facetsA, facetsB); + } + + + protected void writeFacets( + Document doc, + ElementCreator cr, + Element output, + List<Facet> a, + List<Facet> b) + { + int num = a.size(); + + for (int i = 0; i < num; i++) { + ManagedFacet fA = (ManagedFacet) a.get(i); + + if (!mergeFacets(doc, cr, output, fA, b)) { + writeFacet(doc, cr, output, fA); + } + } + } + + + protected boolean mergeFacets( + Document doc, + ElementCreator cr, + Element output, + ManagedFacet a, + List<Facet> list) + { + String name = a.getName(); + + if (list == null) { + return false; + } + + for (Facet facet: list) { + if (name.equals(facet.getName())) { + writeFacet(doc, cr, output, (ManagedFacet) facet); + return true; + } + } + + return false; + } + + + protected void writeFacet( + Document doc, + ElementCreator cr, + Node output, + ManagedFacet f) + { + Element theme = cr.create("theme"); + cr.addAttr(theme, "artifact", f.getArtifact(), true); + cr.addAttr(theme, "facet", f.getName(), true); + cr.addAttr(theme, "pos", Integer.toString(f.getPosition()), true); + cr.addAttr(theme, "active", Integer.toString(f.getActive()), true); + + output.appendChild(theme); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,754 @@ +package de.intevation.flys.collections; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.ClientProtocolUtils; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.Backend; +import de.intevation.artifactdatabase.Backend.PersistentArtifact; +import de.intevation.artifactdatabase.DefaultArtifactCollection; + +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.model.ManagedFacet; +import de.intevation.flys.exports.OutGenerator; +import de.intevation.flys.themes.Theme; +import de.intevation.flys.themes.ThemeFactory; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class FLYSArtifactCollection extends DefaultArtifactCollection { + + /** The logger used in this class.*/ + private static Logger log = Logger.getLogger(FLYSArtifactCollection.class); + + + /** Constant XPath that points to the outputmodes of an artifact.*/ + public static final String XPATH_ARTIFACT_OUTPUTMODES = + "/art:result/art:outputmodes"; + + public static final String XPATH_COLLECTION_ITEMS = + "/art:result/art:artifact-collection/art:collection-item"; + + public static final String XPATH_OUT_NAME = "/art:action/@art:name"; + + public static final String XPATH_OUT_TYPE = "/art:action/@art:type"; + + + + @Override + public Document describe(CallContext context) { + log.debug("FLYSArtifactCollection.describe: " + identifier); + + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Date creationTime = getCreationTime(); + String creation = creationTime != null + ? Long.toString(creationTime.getTime()) + : ""; + + Element collection = ec.create("artifact-collection"); + Element artifacts = ec.create("artifacts"); + Element attributes = ec.create("attribute"); + + ec.addAttr(collection, "name", getName(), true); + ec.addAttr(collection, "uuid", identifier(), true); + ec.addAttr(collection, "creation", creation, true); + + collection.appendChild(artifacts); + collection.appendChild(attributes); + doc.appendChild(collection); + + ArtifactDatabase db = context.getDatabase(); + + try { + String[] artifactUUIDs = getArtifactUUIDs(context); + + Document oldAttrs = getAttribute(); + Document attrs = buildAttributes( + db, context, + oldAttrs, + artifactUUIDs); + + db.setCollectionAttribute(identifier(), context.getMeta(), attrs); + + Node child = attrs.getFirstChild(); + attributes.appendChild(doc.importNode(child, true)); + + for (String uuid: artifactUUIDs) { + try { + artifacts.appendChild( + buildArtifactNode(db, uuid, context, ec)); + } + catch (ArtifactDatabaseException dbe) { + log.warn(dbe, dbe); + } + } + } + catch (ArtifactDatabaseException ade) { + log.error(ade, ade); + } + + return doc; + } + + + @Override + public void out(Document format, OutputStream out, CallContext context) + throws IOException + { + log.info("FLYSArtifactCollection.out"); + + String name = XMLUtils.xpathString( + format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE); + + String type = XMLUtils.xpathString( + format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE); + + log.debug("Output name = " + name); + log.debug("Output type = " + type); + + OutGenerator generator = getOutGenerator(context, name, type); + if (generator == null) { + log.error("There is no generator specified for output: " + name); + // TODO throw an exception. + + return; + } + + generator.init(format, out, context); + + // TODO Determine the correct master artifact here! + + try { + doOut(generator, name, type, getAttribute(context, name), context); + } + catch (ArtifactDatabaseException adbe) { + log.error(adbe, adbe); + } + } + + + /** + * This method creates the concrete output. + * + * @param generator The OutGenerator that creates the output. + * @param outputName The name of the requested output. + * @param attributes The collection's attributes for this concrete output + * type. + * @param context The context object. + */ + protected void doOut( + OutGenerator generator, + String outName, + String facet, + Document attributes, + CallContext context) + throws IOException + { + log.debug("FLYSArtifactCollection.doOut: " + outName); + + ThemeList themeList = new ThemeList(attributes); + + int size = themeList.size(); + log.debug("Output will contain " + size + " elements."); + + try { + for (int i = 0; i < size; i++) { + ManagedFacet theme = themeList.get(i); + + if (theme == null) { + log.debug("Theme is empty - no output is generated."); + continue; + } + + String art = theme.getArtifact(); + + if (log.isDebugEnabled()) { + log.debug("Do output for..."); + log.debug("... artifact: " + art); + log.debug("... facet: " + theme.getName()); + } + + String facetName = theme.getName(); + + if (outName.equals("export") && !facetName.equals(facet)) { + continue; + } + + // TODO Remove the following two lines of code! The master + // artifact has to be determined correctly after + // OutGenerator.init is called! + Artifact artifact = getArtifact(art, context); + generator.setMasterArtifact(artifact); + + generator.doOut( + artifact, + facetName, + getFacetThemeFromAttribute( + art, + outName, + facetName, + context)); + } + } + catch (ArtifactDatabaseException ade) { + log.error(ade, ade); + } + + generator.generate(); + } + + + protected Document buildAttributes( + ArtifactDatabase db, + CallContext context, + Document oldAttr, + String[] items) + { + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + AttributeParser aParser = new AttributeParser(); + OutputParser oParser = new OutputParser(db, context.getMeta()); + + for (String uuid: items) { + try { + oParser.parse(uuid); + } + catch (ArtifactDatabaseException ade) { + log.warn(ade, ade); + } + } + + aParser.parse(oldAttr); + + return new AttributeWriter(aParser.getOuts(), oParser.getOuts()).write(); + } + + + /** + * Returns the attribute for a specific output type. + * + * @param output The name of the desired output type. + * + * @return the attribute for the desired output type. + */ + protected Document getAttribute(CallContext context, String output) + throws ArtifactDatabaseException + { + Document attr = buildAttributes( + context.getDatabase(), + context, + getAttribute(), + getArtifactUUIDs(context)); + + Node out = (Node) XMLUtils.xpath( + attr, + "art:outputs/art:output[@name='" + output + "']", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + + if (out != null) { + Document o = XMLUtils.newDocument(); + + o.appendChild(o.importNode(out, true)); + + return o; + } + + return null; + } + + + /** + * This method returns the list of artifact UUIDs that this collections + * contains. + * + * @param context The CallContext that is necessary to get information about + * the ArtifactDatabase. + * + * @return a list of uuids. + */ + protected String[] getArtifactUUIDs(CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getArtifactUUIDs"); + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + Document itemList = db.listCollectionArtifacts(identifier(), meta); + NodeList items = (NodeList) XMLUtils.xpath( + itemList, + XPATH_COLLECTION_ITEMS, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + if (items == null || items.getLength() == 0) { + log.debug("No artifacts found in this collection."); + return null; + } + + int num = items.getLength(); + + List<String> uuids = new ArrayList<String>(num); + + for (int i = 0; i < num; i++) { + String uuid = XMLUtils.xpathString( + items.item(i), + "@art:uuid", + ArtifactNamespaceContext.INSTANCE); + + if (uuid != null && uuid.trim().length() != 0) { + uuids.add(uuid); + } + } + + return (String[]) uuids.toArray(new String[uuids.size()]); + } + + + /** + * Returns a concrete Artifact of this collection specified by its uuid. + * + * @param uuid The Artifact's uuid. + * @param context The CallContext. + * + * @return an Artifact. + */ + protected Artifact getArtifact(String uuid, CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getArtifact"); + + Backend backend = Backend.getInstance(); + PersistentArtifact persistent = backend.getArtifact(uuid); + + return persistent != null ? persistent.getArtifact() : null; + } + + + /** + * Returns the attribute that belongs to an artifact stored in this + * collection. + * + * @param uuid The Artifact's uuid. + * @param outname The name of the requested output. + * @param facet The name of the requested facet. + * @param context The CallContext. + * + * @return an attribute in form of a document. + */ + protected Document getFacetThemeFromAttribute( + String uuid, + String outName, + String facet, + CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getFacetThemeFromAttribute"); + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + Map<String, String> mappings = (Map<String, String>) + flysContext.get(FLYSContext.THEME_MAPPING); + + String themeName = mappings.get(facet); + + Document attr = db.getCollectionItemAttribute(identifier(), uuid, meta); + + if (attr == null) { + attr = initItemAttribute(uuid, facet, context); + + if (attr == null) { + return null; + } + } + + log.debug("Search attribute of collection item: " + uuid); + + Node tmp = (Node) XMLUtils.xpath( + attr, + "/art:attribute", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (tmp == null) { + log.warn("No attribute found. Operation failed."); + return null; + } + + log.debug("Search theme '" + themeName + "' in attribute."); + + Node theme = (Node) XMLUtils.xpath( + tmp, + "art:themes/theme[@name='" + themeName + "']", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (theme == null) { + log.warn("Could not find the theme in attribute of: " + uuid); + + Theme t = getThemeForFacet(uuid, facet, context); + + if (t == null) { + log.warn("No theme found for facet: " + facet); + return null; + } + + addThemeToAttribute(uuid, attr, t, context); + theme = t.toXML().getFirstChild(); + } + + Document doc = XMLUtils.newDocument(); + doc.appendChild(doc.importNode(theme, true)); + + return doc; + } + + + /** + * Adds the theme of a facet to a CollectionItem's attribute. + * + * @param uuid The uuid of the artifact. + * @param attr The current attribute of an artifact. + * @param t The theme to add. + * @param context The CallContext. + */ + protected void addThemeToAttribute( + String uuid, + Document attr, + Theme t, + CallContext context) + { + log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid); + + if (t == null) { + log.warn("Theme is empty - cancel adding it to attribute!"); + return; + } + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + attr, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Node tmp = (Node) XMLUtils.xpath( + attr, + "/art:attribute", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (tmp == null) { + tmp = ec.create("attribute"); + attr.appendChild(tmp); + } + + Node themes = (Node) XMLUtils.xpath( + tmp, + "art:themes", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (themes == null) { + themes = ec.create("themes"); + tmp.appendChild(themes); + } + + themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true)); + + try { + setCollectionItemAttribute(uuid, attr, context); + + log.debug("Successfully added theme to item attribute."); + } + catch (ArtifactDatabaseException e) { + // do nothing + log.warn("Cannot set attribute of item: " + uuid); + } + } + + + /** + * Initializes the attribute of an collection item with the theme of a + * specific facet. + * + * @param uuid The uuid of an artifact. + * @param facet The name of a facet. + * @param context The CallContext. + * + * @param the new attribute. + */ + protected Document initItemAttribute( + String uuid, + String facet, + CallContext context) + { + log.info("FLYSArtifactCollection.initItemAttribute"); + + Theme t = getThemeForFacet(uuid, facet, context); + + if (t == null) { + log.info("Could not find theme for facet. Cancel initialization."); + return null; + } + + Document attr = XMLUtils.newDocument(); + + addThemeToAttribute(uuid, attr, t, context); + + return attr; + } + + + /** + * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new + * value <i>attr</i>. + * + * @param uuid The uuid of the CollectionItem. + * @param attr The new attribute for the CollectionItem. + * @param context The CallContext. + */ + public void setCollectionItemAttribute( + String uuid, + Document attr, + CallContext context) + throws ArtifactDatabaseException + { + Document doc = ClientProtocolUtils.newSetItemAttributeDocument( + uuid, + attr); + + if (doc == null) { + log.warn("Cannot set item attribute: No attribute found."); + return; + } + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + db.setCollectionItemAttribute(identifier(), uuid, doc, meta); + } + + + /** + * Returns the theme of a specific facet. + * + * @param uuid The uuid of an artifact. + * @param facet The name of the facet. + * @param context The CallContext object. + * + * @return the desired theme. + */ + protected Theme getThemeForFacet( + String uuid, + String facet, + CallContext context) + { + log.info("FLYSArtifactCollection.getThemeForFacet: " + facet); + + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + return ThemeFactory.getTheme(flysContext, facet); + } + + + /** + * Returns the OutGenerator for a specified <i>type</i>. + * + * @param name The name of the output type. + * @param type Defines the type of the desired OutGenerator. + * + * @return The OutGenerator specified by <i>type</i>. + */ + protected OutGenerator getOutGenerator( + CallContext context, + String name, + String type) + { + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + Map<String, Class> generators = (Map<String, Class>) + flysContext.get(FLYSContext.OUTGENERATORS_KEY); + + if (generators == null) { + log.error("No output generators found in the running application!"); + return null; + } + + Class clazz = generators.get(name); + + try { + return clazz != null ? (OutGenerator) clazz.newInstance() : null; + } + catch (InstantiationException ie) { + log.error(ie, ie); + } + catch (IllegalAccessException iae) { + log.error(iae, iae); + } + + return null; + } + + + protected Element buildArtifactNode( + ArtifactDatabase database, + String uuid, + CallContext context, + XMLUtils.ElementCreator ec) + throws ArtifactDatabaseException + { + log.debug("Append artifact '" + uuid + "' to collection description"); + + // TODO + String hash = "MYHASH"; + + Element ci = ec.create("artifact"); + ec.addAttr(ci, "uuid", uuid, true); + ec.addAttr(ci, "hash", hash, true); + + // XXX I am not sure if it works well every time with an empty document + // in the describe operation of an artifact. + Document description = database.describe(uuid, null, context.getMeta()); + + Node outputModes = (Node) XMLUtils.xpath( + description, + XPATH_ARTIFACT_OUTPUTMODES, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (outputModes != null) { + Document doc = ci.getOwnerDocument(); + ci.appendChild(doc.importNode(outputModes, true)); + } + + return ci; + } + + + /** + * Inner class to structure/order the themes of a chart. + */ + private class ThemeList { + private Logger logger = Logger.getLogger(ThemeList.class); + protected Vector<ManagedFacet> themes; + + public ThemeList(Document output) { + themes = new Vector<ManagedFacet>(); + parse(output); + } + + protected void parse(Document output) { + NodeList themes = (NodeList) XMLUtils.xpath( + output, + "art:output/art:theme", + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = themes != null ? themes.getLength() : 0; + + logger.debug("Output has " + num + " elements."); + + if (num == 0) { + return; + } + + for (int i = 0; i < num; i++) { + Node theme = themes.item(i); + + String name = XMLUtils.xpathString( + theme, "@art:facet", ArtifactNamespaceContext.INSTANCE); + + String uuid = XMLUtils.xpathString( + theme, "@art:artifact", ArtifactNamespaceContext.INSTANCE); + + String pos = XMLUtils.xpathString( + theme, "@art:pos", ArtifactNamespaceContext.INSTANCE); + + String active = XMLUtils.xpathString( + theme, "@art:active", ArtifactNamespaceContext.INSTANCE); + + addTheme(uuid, name, pos, active); + } + } + + protected void addTheme( + String uuid, + String name, + String position, + String active) + { + if (logger.isDebugEnabled()) { + logger.debug("Add theme: "); + logger.debug(".. name: " + name); + logger.debug(".. uuid: " + uuid); + logger.debug(".. position: " + position); + logger.debug(".. active: " + active); + } + + try { + int pos = Integer.parseInt(position); + int act = Integer.parseInt(active); + + themes.add(pos-1, new ManagedFacet(name, null, uuid, pos, act)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + public ManagedFacet get(int idx) { + return themes.get(idx); + } + + public int size() { + return themes.size(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/OutputParser.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,141 @@ +package de.intevation.flys.collections; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.Output; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.model.ManagedFacet; + + +public class OutputParser { + + /** Constant XPath that points to the outputmodes of an artifact.*/ + public static final String XPATH_ARTIFACT_OUTPUTMODES = + "/art:result/art:outputmodes/art:output"; + + + private static Logger logger = Logger.getLogger(OutputParser.class); + + protected ArtifactDatabase db; + protected CallMeta meta; + + protected Map<String, Output> outs; + + + public OutputParser(ArtifactDatabase db, CallMeta meta) { + this.db = db; + this.meta = meta; + this.outs = new HashMap<String, Output>(); + } + + + public void parse(String uuid) + throws ArtifactDatabaseException + { + logger.debug("OutputParser.parse: " + uuid); + + // XXX I am not sure if it works well every time with an empty + // document in the describe operation of an artifact. + Document description = db.describe(uuid, null, meta); + + NodeList outs = (NodeList) XMLUtils.xpath( + description, + XPATH_ARTIFACT_OUTPUTMODES, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = outs != null ? outs.getLength() : 0; + + logger.debug("Artifact has " + num + " outputs."); + + for (int i = 0; i < num; i++) { + Node out = outs.item(i); + + parseOutput(uuid, out); + } + } + + + public Map<String, Output> getOuts() { + return outs; + } + + + protected void addItem(String out, ManagedFacet item) { + Output o = outs.get(out); + + if (o != null) { + int num = o.getFacets().size(); + item.setPosition(num+1); + + o.addFacet(item); + } + } + + + protected void parseOutput(String uuid, Node out) { + String name = XMLUtils.xpathString( + out, "@art:name", ArtifactNamespaceContext.INSTANCE); + + if (outs.get(name) == null) { + logger.debug("Create new output: " + name); + newOutput(out, name); + } + + parseItems(uuid, out, name); + } + + + protected void newOutput(Node out, String name) { + String desc = XMLUtils.xpathString( + out, "@art:description", ArtifactNamespaceContext.INSTANCE); + + String mimetype = XMLUtils.xpathString( + out, "@art:mime-type", ArtifactNamespaceContext.INSTANCE); + + Output o = new DefaultOutput(name, desc, mimetype); + + outs.put(name, o); + } + + + protected void parseItems(String uuid, Node out, String outname) { + NodeList facets = (NodeList) XMLUtils.xpath( + out, "art:facets/art:facet", + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = facets != null ? facets.getLength() : 0; + + logger.debug("Output has " + num + " facets."); + + for (int i = 0; i < num; i++) { + Node facet = facets.item(i); + + String name = XMLUtils.xpathString( + facet, "@art:name", ArtifactNamespaceContext.INSTANCE); + + ManagedFacet item = new ManagedFacet(name, null, uuid, 1, 1); + + addItem(outname, item); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/AbstractExporter.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,196 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.text.NumberFormat; +import java.util.Locale; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import au.com.bytecode.opencsv.CSVWriter; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * An abstract exporter that implements some basic methods for exporting data of + * artifacts. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class AbstractExporter implements OutGenerator { + + /** The logger used in this exporter.*/ + private static Logger logger = Logger.getLogger(AbstractExporter.class); + + + /** The name of the CSV facet which triggers the CSV creation.*/ + public static final String FACET_CSV = "csv"; + + /** The default charset for the CSV export.*/ + public static final String DEFAULT_CSV_CHARSET = "UTF-8"; + + /** The default separator for the CSV export.*/ + public static final char DEFAULT_CSV_SEPARATOR = ','; + + + /** The document of the incoming out() request.*/ + protected Document request; + + /** The output stream where the data should be written to.*/ + protected OutputStream out; + + /** The CallContext object.*/ + protected CallContext context; + + /** The selected facet.*/ + protected String facet; + + /** The master artifact.*/ + protected Artifact master; + + + /** + * Concrete subclasses need to use this method to write their special data + * objects into the CSV document. + * + * @param writer The CSVWriter. + */ + protected abstract void writeCSVData(CSVWriter writer); + + + /** + * This method enables concrete subclasses to collected its own special + * data. + * + * @param artifacts The artifact that stores the data that has to be + * exported. + */ + protected abstract void addData(Artifact artifact); + + + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("AbstractExporter.init"); + + this.request = request; + this.out = out; + this.context = context; + } + + + public void setMasterArtifact(Artifact master) { + this.master = master; + } + + + /** + * This doOut() just collects the data of multiple artifacts. Therefore, it + * makes use of the addData() method which enables concrete subclasses to + * store its data on its own. The real output creation takes place in the + * concrete generate() methods. + * + * @param artifact The artifact. + * @param facet The facet to add - NOTE: the facet needs to fit to the first + * facet inserted into this exporter. Otherwise this artifact/facet is + * skipped. + * @param attr The attr document. + */ + public void doOut(Artifact artifact, String facet, Document attr) { + logger.debug("AbstractExporter.doOut: " + facet); + + if (!isFacetValid(facet)) { + logger.warn("Facet '" + facet + "' not valid. No output created!"); + return; + } + + addData(artifact); + } + + + /** + * Generates an export based on a specified facet. + */ + public void generate() + throws IOException + { + logger.debug("AbstractExporter.generate"); + + if (facet != null && facet.equals(FACET_CSV)) { + generateCSV(); + } + else { + throw new IOException("invalid facet for exporter."); + } + } + + + /** + * Determines if the desired facet is valid for this exporter. If no facet + * is currently set, <i>facet</i> is set. + * + * @param facet The desired facet. + * + * @return true, if <i>facet</i> is valid, otherwise false. + */ + protected boolean isFacetValid(String facet) { + logger.debug("AbstractExporter.isFacetValid"); + + if (facet == null || facet.length() == 0) { + return false; + } + else if (this.facet == null || this.facet.length() == 0) { + logger.debug("Set the facet of this export: " + facet); + this.facet = facet; + + return true; + } + else { + return this.facet.equals(facet); + } + } + + + + protected String msg(String key, String def) { + return Resources.getMsg(context.getMeta(), key, def); + } + + + /** + * This method starts CSV creation. It makes use of writeCSVData() which has + * to be implemented by concrete subclasses. + */ + protected void generateCSV() + throws IOException + { + logger.info("AbstractExporter.generateCSV"); + + CSVWriter writer = new CSVWriter( + new OutputStreamWriter( + out, + DEFAULT_CSV_CHARSET), + DEFAULT_CSV_SEPARATOR); + + writeCSVData(writer); + + writer.close(); + } + + + protected NumberFormat getFormatter(int min, int max) { + Locale locale = Resources.getLocale(context.getMeta()); + NumberFormat nf = NumberFormat.getInstance(locale); + + nf.setMaximumFractionDigits(max); + nf.setMinimumFractionDigits(min); + + return nf; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartExportHelper.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.flys.exports; + +import com.lowagie.text.Document; +import com.lowagie.text.DocumentException; +import com.lowagie.text.PageSize; +import com.lowagie.text.Rectangle; + +import com.lowagie.text.pdf.PdfContentByte; +import com.lowagie.text.pdf.PdfTemplate; +import com.lowagie.text.pdf.PdfWriter; + +import java.awt.Graphics2D; +import java.awt.Transparency; + +import java.awt.geom.Rectangle2D.Double; + +import java.awt.geom.Rectangle2D; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; + +import javax.imageio.ImageIO; + +import org.apache.batik.svggen.SVGGraphics2D; +import org.apache.batik.svggen.SVGGraphics2DIOException; + +import org.apache.log4j.Logger; + +import org.jfree.chart.JFreeChart; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * This class is a helper class which supports some methods to export charts + * into specific formats. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ChartExportHelper { + + /** + * Constant field to define A4 as default page size. + */ + private static final String DEFAULT_PAGE_SIZE = "A4"; + + /** + * Constant field to define UTF-8 as default encoding. + */ + private static final String DEFAULT_ENCODING = "UTF-8"; + + /** + * Logger used for logging with log4j. + */ + private static Logger log = Logger.getLogger(ChartExportHelper.class); + + + /** + * A method to export a <code>JFreeChart</code> as image to an + * <code>OutputStream</code> with a given format, width and height. + * + * @param out OutputStream + * @param chart JFreeChart object to be exported. + * @param format Format (e.g. png, gif, jpg) + * @param width Width, the image used to be + * @param height Height, the image used to be + * + * @throws IOException if writing image to OutputStream failed. + */ + public static void exportImage( + OutputStream out, + JFreeChart chart, + String format, + int width, + int height + ) + throws IOException + { + log.info("export chart as png"); + + ImageIO.write( + chart.createBufferedImage( + width, height, Transparency.BITMASK, null + ), + format, + out + ); + } + + + /** + * A method to export a <code>JFreeChart</code> as SVG to an + * <code>OutputStream</code>. + * + * @param out OutputStream + * @param chart JFreeChart to be exported + * @param encoding Encoding, defaults to {@link #DEFAULT_ENCODING} if null + * @param width Width the svg used to be + * @param height Height the svg used to be + */ + public static void exportSVG( + OutputStream out, + JFreeChart chart, + String encoding, + int width, + int height + ) { + log.info("export chart as svg"); + + if (encoding == null) + encoding = DEFAULT_ENCODING; + + org.w3c.dom.Document document = XMLUtils.newDocument(); + SVGGraphics2D graphics = new SVGGraphics2D(document); + + chart.draw(graphics, new Rectangle2D.Double(0.0D, 0.0D,width,height)); + + try { + graphics.stream(new OutputStreamWriter(out, encoding)); + } + catch (SVGGraphics2DIOException svge) { + log.error("Error while writing svg export to output stream.", svge); + } + catch (UnsupportedEncodingException uee) { + log.error("Unsupported encoding: " + encoding, uee); + } + } + + + /** + * A method to export a <code>JFreeChart</code> as PDF to an + * <code>OutputStream</code>. + * + * @param out OutputStream + * @param chart JFreeChart + * @param pageFormat String to specify a page format, {@link + * #DEFAULT_PAGE_SIZE} is used if no pageFormat is given + * @param landscape If this is true, the pdf is delivered in landscape + * format + * @param marginLeft Space to left border + * @param marginRight Space to right border + * @param marginTop Space to upper border + * @param marginBottom Space to lower border + */ + public static void exportPDF( + OutputStream out, + JFreeChart chart, + String pageFormat, + float marginLeft, + float marginRight, + float marginTop, + float marginBottom, + CallContext context + ) { + log.info("export chart as pdf."); + + if (pageFormat == null) + pageFormat = DEFAULT_PAGE_SIZE; + + // max size of the chart + Rectangle page = PageSize.getRectangle(pageFormat); + float pageWidth = page.getWidth(); + float pageHeight = page.getHeight(); + + // the chart width + int chartWidth = (Integer) context.getContextValue("chart.width"); + int chartHeight = (Integer) context.getContextValue("chart.height"); + + boolean landscape = chartWidth > chartHeight ? true : false; + + float width = 0; + float height = 0; + if (landscape) { + width = pageHeight; + height = pageWidth; + } + else { + width = pageWidth; + height = pageHeight; + } + + float spaceX = width - marginLeft - marginRight; + if (chartWidth > spaceX) { + log.warn("Width of the chart is too big for pdf -> resize it now."); + double ratio = ((double)spaceX) / chartWidth; + chartWidth *= ratio; + chartHeight *= ratio; + log.debug("Resized chart to " + chartWidth + "x" + chartHeight); + } + + float spaceY = height - marginTop - marginBottom; + if (chartHeight > spaceY) { + log.warn("Height of the chart is too big for pdf -> resize it now."); + double ratio = ((double)spaceY) / chartHeight; + chartWidth *= ratio; + chartHeight *= ratio; + log.debug("Resized chart to " + chartWidth + "x" + chartHeight); + } + + Document document = null; + if (landscape) { + document = new Document(page.rotate()); + log.debug("Create landscape pdf."); + } + else + document = new Document(page); + + try { + PdfWriter writer = PdfWriter.getInstance(document, out); + + document.addSubject(chart.getTitle().getText()); + document.addCreationDate(); + document.open(); + + PdfContentByte content = writer.getDirectContent(); + + PdfTemplate template = content.createTemplate(width, height); + Graphics2D graphics = template.createGraphics(width, height); + + double[] origin = getCenteredAnchor( + marginLeft, marginRight, marginBottom, marginTop, + width, height, + chartWidth, chartHeight); + + Rectangle2D area = new Rectangle2D.Double( + origin[0], origin[1], chartWidth, chartHeight); + + chart.draw(graphics, area); + graphics.dispose(); + content.addTemplate(template, 0f, 0f); + } + catch (DocumentException de) { + log.error("Error while exporting chart to pdf.", de); + } + finally { + document.close(); + } + } + + + /** + * This method returns the anchor of the chart so that the chart is centered + * according to the given parameters. + * + * @param mLeft Left margin + * @param mRight Right margin + * @param mBottom Bottom margin + * @param mTop Top margin + * @param width The complete width of the drawing area. + * @param height The complete height of the drawing area. + * @param chartWidth The width of the chart. + * @param chartHeight The height of the chart. + * + * @return an array that contains the anchor for a chart with the given + * parameters. The first value is the x point, the second value is the y + * point. + */ + public static double[] getCenteredAnchor( + double mLeft, double mRight, double mBottom, double mTop, + double width, double height, + double chartWidth, double chartHeight + ) { + if (log.isDebugEnabled()) { + log.debug("Calculate centered origin..."); + log.debug("-> PDF width : " + width); + log.debug("-> PDF height : " + height); + log.debug("-> Chart width : " + chartWidth); + log.debug("-> Chart height : " + chartHeight); + log.debug("-> margin left : " + mLeft); + log.debug("-> margin right : " + mRight); + log.debug("-> margin bottom: " + mBottom); + log.debug("-> margin top : " + mTop); + } + + double[] origin = new double[2]; + + double centerX = width / 2; + double centerY = height / 2; + + origin[0] = centerX - chartWidth / 2; + origin[1] = centerY - chartHeight / 2; + + origin[0] = origin[0] >= mLeft ? origin[0] : mLeft; + origin[1] = origin[1] >= mTop ? origin[1] : mTop; + + if (log.isDebugEnabled()) { + log.debug("==> centered left origin: " + origin[0]); + log.debug("==> centered top origin: " + origin[1]); + } + + return origin; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,152 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * The base class for chart creation. It should provide some basic things that + * equal in all chart types. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class ChartGenerator implements OutGenerator { + + private static Logger logger = Logger.getLogger(ChartGenerator.class); + + + /** The default chart width, if no other width is set.*/ + public static final int DEFAULT_CHART_WIDTH = 600; + + /** The default chart height, if no other height is set.*/ + public static final int DEFAULT_CHART_HEIGHT = 400; + + /** The XPath that points to the chart size of the incoming request + * document.*/ + public static final String XPATH_CHART_SIZE = + "/art:action/art:attributes/art:size"; + + + /** The document of the incoming out() request.*/ + protected Document request; + + /** The output stream where the data should be written to.*/ + protected OutputStream out; + + /** The CallContext object.*/ + protected CallContext context; + + /** The artifact that is used to decorate the chart with meta information.*/ + protected Artifact master; + + + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("ChartGenerator.init"); + + this.request = request; + this.out = out; + this.context = context; + } + + + public void setMasterArtifact(Artifact master) { + this.master = master; + } + + + protected String msg(String key, String def) { + return Resources.getMsg(context.getMeta(), key, def); + } + + + protected String msg(String key, String def, Object[] args) { + return Resources.getMsg(context.getMeta(), key, def, args); + } + + + protected String getRiverName() { + FLYSArtifact flys = (FLYSArtifact) master; + + River river = flys.getRiver(); + return river != null ? river.getName() : ""; + } + + + protected double[] getRange() { + FLYSArtifact flys = (FLYSArtifact) master; + + return flys.getDistance(); + } + + + /** + * Returns the size of a chart export as array which has been specified by + * the incoming request document. + * + * @return the size of a chart as [width, height] or the result of + * getDefaultSize() if no width or height are given in the request document. + */ + protected int[] getSize() { + int[] size = new int[2]; + + Node sizeEl = (Node) XMLUtils.xpath( + request, + XPATH_CHART_SIZE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (sizeEl != null) { + String w = XMLUtils.xpathString( + sizeEl, "@art:width", ArtifactNamespaceContext.INSTANCE); + + String h = XMLUtils.xpathString( + sizeEl, "@art:height", ArtifactNamespaceContext.INSTANCE); + + if (w != null && w.length() > 0 && h != null && h.length() > 0) { + try { + size[0] = Integer.parseInt(w); + size[1] = Integer.parseInt(h); + } + catch (NumberFormatException nfe) { + logger.warn("Wrong values for chart width/height."); + } + } + } + + return size[0] > 0 && size[1] > 0 ? size : getDefaultSize(); + } + + + /** + * Returns the default size of a chart export as array. + * + * @return the default size of a chart as [width, height]. + */ + protected int[] getDefaultSize() { + return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT }; + } + + + public abstract void doOut(Artifact artifact, String facet, Document attr); + + public abstract void generate() throws IOException; +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,118 @@ +package de.intevation.flys.exports; + +import java.io.OutputStream; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import au.com.bytecode.opencsv.CSVWriter; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.WQKms; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ComputedDischargeCurveExporter extends AbstractExporter { + + /** The logger used in this exporter.*/ + private static Logger logger = + Logger.getLogger(ComputedDischargeCurveExporter.class); + + + public static final int W_MIN_DIGITS = 2; + public static final int W_MAX_DIGITS = 2; + public static final int Q_MIN_DIGITS = 0; + public static final int Q_MAX_DIGITS = 0; + + public static final String CSV_W_HEADER = + "export.computed.discharge.curve.csv.header.w"; + + public static final String CSV_Q_HEADER = + "export.computed.discharge.curve.csv.header.q"; + + public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; + public static final String DEFAULT_CSV_Q_HEADER = "Q [m³/s]"; + + + protected List<WQKms> data; + + + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("ComputedDischargeCurveExporter.init"); + + super.init(request, out, context); + + this.data = new ArrayList<WQKms>(); + } + + + protected void addData(Artifact artifact) { + WINFOArtifact winfoArtifact = (WINFOArtifact) artifact; + data.add(winfoArtifact.getComputedDischargeCurveData()); + } + + + protected void writeCSVData(CSVWriter writer) { + logger.info("ComputedDischargeCurveExporter.writeData"); + + writeCSVHeader(writer); + + NumberFormat wf = getWFormatter(); + NumberFormat qf = getQFormatter(); + + double[] res = new double[3]; + + for (WQKms wqkms: data) { + int size = wqkms.size(); + + for (int i = 0; i < size; i++) { + res = wqkms.get(i, res); + + writer.writeNext(new String[] { + wf.format(res[0]), + qf.format(res[1]) + }); + } + } + } + + + protected void writeCSVHeader(CSVWriter writer) { + logger.debug("ComputedDischargeCurveExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), + msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER) + }); + } + + + /** + * Returns the number formatter for W values. + * + * @return the number formatter for W values. + */ + protected NumberFormat getWFormatter() { + return getFormatter(W_MIN_DIGITS, W_MAX_DIGITS); + } + + + /** + * Returns the number formatter for Q values. + * + * @return the number formatter for Q values. + */ + protected NumberFormat getQFormatter() { + return getFormatter(Q_MIN_DIGITS, Q_MAX_DIGITS); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,98 @@ +package de.intevation.flys.exports; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.title.TextTitle; + +import de.intevation.artifacts.Artifact; + +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.WQKms; + + +/** + * An OutGenerator that generates discharge curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ComputedDischargeCurveGenerator extends DischargeCurveGenerator { + + /** The logger used in this generator.*/ + private static Logger logger = + Logger.getLogger(ComputedDischargeCurveGenerator.class); + + + public static final String I18N_CHART_TITLE = + "chart.computed.discharge.curve.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.computed.discharge.curve.subtitle"; + + public static final String I18N_YAXIS_LABEL = + "chart.computed.discharge.curve.yaxis.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = "Abflusskurve"; + public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; + + + @Override + protected String getChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + + @Override + protected void addSubtitles(JFreeChart chart) { + double[] dist = getRange(); + + Object[] args = new Object[] { + getRiverName(), + dist[0] + }; + + String subtitle = msg(I18N_CHART_SUBTITLE, "", args); + chart.addSubtitle(new TextTitle(subtitle)); + } + + + @Override + protected String getYAxisLabel() { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + + @Override + public void doOut(Artifact artifact, String facet, Document attr) { + logger.debug("ComputedDischargeCurveGenerator.doOut"); + + WQKms wqkms = getData(artifact); + + int size = wqkms.size(); + + double[][] data = new double[2][size]; + double[] res = new double[3]; + + for (int i = 0; i < size; i++) { + res = wqkms.get(i, res); + + data[0][i] = res[1]; + data[1][i] = res[0]; + } + + // TODO find the correct name + dataset.addSeries("Abflusskurve", data); + } + + + protected WQKms getData(Artifact artifact) { + logger.debug("ComputedDischargeCurveGenerator.getData"); + + WINFOArtifact winfoArtifact = (WINFOArtifact) artifact; + + return winfoArtifact.getComputedDischargeCurveData(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,139 @@ +package de.intevation.flys.exports; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.xy.DefaultXYDataset; + +import de.intevation.artifacts.Artifact; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.DischargeTables; +import de.intevation.flys.artifacts.model.GaugesFactory; + + +/** + * An OutGenerator that generates discharge curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DischargeCurveGenerator extends XYChartGenerator { + + /** The logger used in this generator.*/ + private static Logger logger = + Logger.getLogger(DischargeCurveGenerator.class); + + + public static final String I18N_CHART_TITLE = + "chart.discharge.curve.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.discharge.curve.subtitle"; + + public static final String I18N_XAXIS_LABEL = + "chart.discharge.curve.xaxis.label"; + + public static final String I18N_YAXIS_LABEL = + "chart.discharge.curve.yaxis.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = "Abflusskurven"; + public static final String I18N_XAXIS_LABEL_DEFAULT = "Q [m³/s]"; + public static final String I18N_YAXIS_LABEL_DEFAULT = "W [cm]"; + + + /** The Dataset that contains the data for the chart.*/ + protected DefaultXYDataset dataset; + + + public DischargeCurveGenerator() { + super(); + + this.dataset = new DefaultXYDataset(); + } + + + protected String getChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + + @Override + protected void addSubtitles(JFreeChart chart) { + + } + + + protected String getXAxisLabel() { + return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); + } + + + protected String getYAxisLabel() { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + + protected void addDatasets(JFreeChart chart) { + XYPlot plot = (XYPlot) chart.getPlot(); + plot.setDataset(0, dataset); + } + + + public void doOut(Artifact artifact, String facet, Document attr) { + logger.debug("DischargeCurveGenerator.doOut: " + facet); + + if (!(artifact instanceof FLYSArtifact)) { + logger.error("Artifact is no instance of FLYSArtifact."); + return; + } + + FLYSArtifact flysArtifact = (FLYSArtifact) artifact; + + River river = flysArtifact.getRiver(); + double[] distance = flysArtifact.getDistance(); + + List<double[]> ranges = new ArrayList<double[]>(); + ranges.add(distance); + + List<Gauge> gauges = GaugesFactory.getGauges(river); + List<Gauge> filtered = GaugesFactory.filterRanges(gauges, ranges); + + if (logger.isDebugEnabled()) { + int numGauges = gauges != null ? gauges.size() : 0; + int numFiltered = filtered != null ? filtered.size() : 0; + + logger.debug("++++++++++++++++++++"); + logger.debug("Search gauges for river: " + river.getName()); + logger.debug("-> ... from range: " + distance[0]); + logger.debug("-> ... to range: " + distance[1]); + logger.debug("-> Found " + numGauges + " gauges in total."); + logger.debug("-> Found " + numFiltered + " gauges after filtering."); + logger.debug("++++++++++++++++++++"); + } + + String[] gaugeNames = new String[filtered.size()]; + int idx = 0; + for (Gauge gauge: filtered) { + gaugeNames[idx++] = gauge.getName(); + } + + DischargeTables dt = new DischargeTables(river.getName(), gaugeNames); + Map<String, double[][]> gaugeValues = dt.getValues(100d); + + for (String gauge: gaugeNames) { + double[][] values = gaugeValues.get(gauge); + dataset.addSeries(gauge, values); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,94 @@ +package de.intevation.flys.exports; + +import java.text.NumberFormat; + +import org.apache.log4j.Logger; + +import au.com.bytecode.opencsv.CSVWriter; + +import de.intevation.artifacts.Artifact; + +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.WQCKms; +import de.intevation.flys.artifacts.model.WQKms; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DischargeLongitudinalSectionExporter extends WaterlevelExporter { + + /** The logger used in this exporter.*/ + private static Logger logger = + Logger.getLogger(DischargeLongitudinalSectionExporter.class); + + + public static final String CSV_KM_HEADER = + "export.discharge.longitudinal.section.csv.header.km"; + + public static final String CSV_W_HEADER = + "export.discharge.longitudinal.section.csv.header.w"; + + public static final String CSV_CW_HEADER = + "export.discharge.longitudinal.section.csv.header.cw"; + + public static final String CSV_Q_HEADER = + "export.discharge.longitudinal.section.csv.header.q"; + + public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km"; + public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; + public static final String DEFAULT_CSV_CW_HEADER = "W korr."; + public static final String DEFAULT_CSV_Q_HEADER = "Q [m³/s]"; + + + @Override + protected void addData(Artifact artifact) { + WINFOArtifact winfo = (WINFOArtifact) artifact; + WQKms[] wqkms = winfo.getDischargeLongitudinalSectionData(); + + logger.debug("Got " + wqkms != null ? wqkms.length : 0 + " WQKms obj."); + + data.add(wqkms); + } + + + @Override + protected void writeCSVHeader(CSVWriter writer) { + logger.info("WaterlevelExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), + msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), + msg(CSV_CW_HEADER, DEFAULT_CSV_CW_HEADER), + msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER) + }); + } + + @Override + protected void wQKms2CSV(CSVWriter writer, WQKms wqkms) { + logger.debug("WaterlevelExporter.wQKms2CSV"); + + int size = wqkms.size(); + double[] result = new double[4]; + + NumberFormat kmf = getKmFormatter(); + NumberFormat wf = getWFormatter(); + NumberFormat qf = getQFormatter(); + + for (int i = 0; i < size; i ++) { + result = wqkms.get(i, result); + + String wc = ""; + if (wqkms instanceof WQCKms) { + wc = wf.format(result[3]); + } + + writer.writeNext(new String[] { + kmf.format(result[2]), + wf.format(result[0]), + wc, + qf.format(result[1]) + }); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,180 @@ +package de.intevation.flys.exports; + +import java.awt.Color; + +import org.apache.log4j.Logger; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; + +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.WQCKms; +import de.intevation.flys.artifacts.model.WQKms; + + +/** + * An OutGenerator that generates discharge longitudinal section curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DischargeLongitudinalSectionGenerator +extends LongitudinalSectionGenerator +{ + private static Logger logger = + Logger.getLogger(DischargeLongitudinalSectionGenerator.class); + + + /** The facet for Ws.*/ + public static final String FACET_W = "discharge_longitudinal_section.w"; + + /** The facet for Qs.*/ + public static final String FACET_Q = "discharge_longitudinal_section.q"; + + + /** The storage for the corrected W series to be drawn in this chart.*/ + protected XYSeriesCollection cw; + + + public DischargeLongitudinalSectionGenerator() { + super(); + + this.cw = new XYSeriesCollection(); + } + + + public void addDatasets(JFreeChart chart) { + super.addDatasets(chart); + + XYPlot plot = (XYPlot) chart.getPlot(); + + plot.setDataset(2, cw); + } + + + protected void adjustPlot(XYPlot plot) { + super.adjustPlot(plot); + + // TODO REMOVE THIS CODE, IF WE HAVE INTRODUCED THEMES! + XYLineAndShapeRenderer rw = (XYLineAndShapeRenderer) + plot.getRendererForDataset(w); + + XYLineAndShapeRenderer rcw = null; + try { + rcw = (XYLineAndShapeRenderer) rw.clone(); + } + catch (Exception e) { + logger.error(e, e); + } + + int cwNum = cw.getSeriesCount(); + + for (int i = 0; i < cwNum; i++) { + rcw.setSeriesPaint(i, Color.RED); + } + + plot.setRenderer(2, rcw); + } + + + + @Override + public void doOut(Artifact artifact, String facet, Document attr) { + logger.debug("DischargeLongitudinalSectionGenerator.doOut"); + + if (facet != null && facet.equals(FACET_W)) { + doWOut(getDischargeLongitudinalSectionData(artifact)); + } + else if (facet != null && facet.equals(FACET_Q)) { + doQOut(getDischargeLongitudinalSectionData(artifact)); + } + else { + logger.warn("Unknown facet name: " + facet); + return; + } + } + + + protected WQKms[] getDischargeLongitudinalSectionData(Artifact artifact) { + WINFOArtifact winfoArtifact = (WINFOArtifact) artifact; + return winfoArtifact.getDischargeLongitudinalSectionData(); + } + + + /** + * This method adds new Series for the wqkms objects. If there are instances + * of WQCKms in this array, there is a further curve that displays the + * corrected W values. + * + * @param wqkms An array of WQKms objects to be displayed. + */ + protected void doWOut(WQKms[] wqkms) { + logger.debug("LongitudinalSectionGenerator.doWOut"); + + int idx = 0; + for (WQKms tmp: wqkms) { + if (tmp instanceof WQCKms) { + doCorrectedWOut((WQCKms) tmp); + } + + doWOut(tmp); + } + } + + + /** + * Adds a new series for the W curve. + * + * @param wqkms The object that contains the W values. + */ + protected void doWOut(WQKms wqkms) { + logger.debug("DischargeLongitudinalSectionGenerator.doWOut"); + + // TODO CREATE CORRECT SERIES NAME + XYSeries series = new XYSeries("W"); + + double[] target = new double[4]; + int size = wqkms.size(); + + for (int i = 0; i < size; i++) { + target = wqkms.get(i, target); + + series.add(target[2], target[0]); + } + + w.addSeries(series); + } + + + /** + * Adds a new series for the corrected W curve. + * + * @param wqckms The object that contains the corrected W values. + */ + protected void doCorrectedWOut(WQCKms wqckms) { + logger.debug("DischargeLongitudinalSectionGenerator.doCorrectedWOut"); + + // TODO CREATE CORRECT SERIES NAME + XYSeries series = new XYSeries("Korrigiert"); + + double[] target = new double[4]; + int size = wqckms.size(); + + for (int i = 0; i < size; i++) { + target = wqckms.get(i, target); + + series.add(target[2], target[3]); + } + + if (series.getItemCount() > 0) { + cw.addSeries(series); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveExporter.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,139 @@ +package de.intevation.flys.exports; + +import java.io.OutputStream; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import au.com.bytecode.opencsv.CSVWriter; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.WQDay; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DurationCurveExporter extends AbstractExporter { + + /** The logger used in this exporter.*/ + private static Logger logger = Logger.getLogger(WaterlevelExporter.class); + + + public static final int W_MIN_DIGITS = 0; + public static final int W_MAX_DIGITS = 2; + public static final int Q_MIN_DIGITS = 0; + public static final int Q_MAX_DIGITS = 1; + public static final int D_MIN_DIGITS = 0; + public static final int D_MAX_DIGITS = 0; + + public static final String CSV_DURATION_HEADER = + "export.duration.curve.csv.header.duration"; + + public static final String CSV_W_HEADER = + "export.duration.curve.csv.header.w"; + + public static final String CSV_Q_HEADER = + "export.duration.curve.csv.header.q"; + + public static final String DEFAULT_CSV_DURATION_HEADER = "D [Tagen]"; + public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; + public static final String DEFAULT_CSV_Q_HEADER = "Q [m³/s]"; + + + /** The storage that contains all WQKms objects for the different facets.*/ + protected List<WQDay> data; + + + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("DurationCurveExporter.init"); + + super.init(request, out, context); + + this.data = new ArrayList<WQDay>(); + } + + + protected void addData(Artifact artifact) { + WINFOArtifact winfoArtifact = (WINFOArtifact) artifact; + data.add(winfoArtifact.getDurationCurveData()); + } + + + protected void writeCSVData(CSVWriter writer) { + logger.info("DurationCurveExporter.writeData"); + + writeCSVHeader(writer); + + for (WQDay wqday: data) { + wQDay2CSV(writer, wqday); + } + } + + + protected void writeCSVHeader(CSVWriter writer) { + logger.info("DurationCurveExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), + msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER), + msg(CSV_DURATION_HEADER, DEFAULT_CSV_DURATION_HEADER) + }); + } + + + protected void wQDay2CSV(CSVWriter writer, WQDay wqday) { + logger.debug("DurationCurveExporter.wQDay2CSV"); + + int size = wqday.size(); + + NumberFormat wf = getWFormatter(); + NumberFormat qf = getQFormatter(); + NumberFormat df = getDFormatter(); + + for (int i = 0; i < size; i ++) { + writer.writeNext(new String[] { + wf.format(wqday.getW(i)), + qf.format(wqday.getQ(i)), + df.format(wqday.getDay(i)) + }); + } + } + + + /** + * Returns the number formatter for W values. + * + * @return the number formatter for W values. + */ + protected NumberFormat getWFormatter() { + return getFormatter(W_MIN_DIGITS, W_MAX_DIGITS); + } + + + /** + * Returns the number formatter for Q values. + * + * @return the number formatter for Q values. + */ + protected NumberFormat getQFormatter() { + return getFormatter(Q_MIN_DIGITS, Q_MAX_DIGITS); + } + + + /** + * Returns the number formatter for duration values. + * + * @return the number formatter for duration values. + */ + protected NumberFormat getDFormatter() { + return getFormatter(D_MIN_DIGITS, D_MAX_DIGITS); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,234 @@ +package de.intevation.flys.exports; + +import java.awt.Color; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.title.TextTitle; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import de.intevation.artifacts.Artifact; + +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.WQDay; + + +/** + * An OutGenerator that generates duration curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DurationCurveGenerator extends XYChartGenerator { + + private static Logger logger = + Logger.getLogger(DurationCurveGenerator.class); + + /** The storage for the W series to be drawn in this chart.*/ + protected XYSeriesCollection w; + + /** The storage for the Q series to be drawn in this chart.*/ + protected XYSeriesCollection q; + + + public static final String DURATION_CURVE_W = + "duration_curve.w"; + + public static final String DURATION_CURVE_Q = + "duration_curve.q"; + + public static final String I18N_CHART_TITLE = + "chart.duration.curve.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.duration.curve.subtitle"; + + public static final String I18N_XAXIS_LABEL = + "chart.duration.curve.xaxis.label"; + + public static final String I18N_YAXIS_LABEL = + "chart.duration.curve.yaxis.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = + "Dauerlinie"; + + public static final String I18N_XAXIS_LABEL_DEFAULT = + "Unterschreitungsdauer [Tage]"; + + public static final String I18N_YAXIS_LABEL_DEFAULT = + "W [NN + m]"; + + + public DurationCurveGenerator() { + super(); + + this.w = new XYSeriesCollection(); + this.q = new XYSeriesCollection(); + } + + + protected String getChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + + @Override + protected void addSubtitles(JFreeChart chart) { + double[] dist = getRange(); + + Object[] args = new Object[] { + getRiverName(), + dist[0] + }; + + String subtitle = msg(I18N_CHART_SUBTITLE, "", args); + chart.addSubtitle(new TextTitle(subtitle)); + } + + + protected String getXAxisLabel() { + return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); + } + + + protected String getYAxisLabel() { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + + public void addDatasets(JFreeChart chart) { + XYPlot plot = (XYPlot) chart.getPlot(); + + plot.setDataset(0, w); + plot.setDataset(1, q); + } + + + protected void adjustPlot(XYPlot plot) { + super.adjustPlot(plot); + + // TODO REMOVE THIS CODE, IF WE HAVE INTRODUCED THEMES! + XYLineAndShapeRenderer rw = (XYLineAndShapeRenderer) + plot.getRendererForDataset(w); + + XYLineAndShapeRenderer rq = null; + try { + rq = (XYLineAndShapeRenderer) rw.clone(); + } + catch (Exception e) { + logger.error(e, e); + } + + int wNum = w.getSeriesCount(); + int qNum = q.getSeriesCount(); + + for (int i = 0; i < wNum; i++) { + rw.setSeriesPaint(i, Color.BLUE); + } + + for (int i = 0; i < qNum; i++) { + rq.setSeriesPaint(i, Color.GREEN); + } + + plot.setRenderer(0, rw); + plot.setRenderer(1, rq); + } + + + protected void adjustAxes(XYPlot plot) { + super.adjustAxes(plot); + + NumberAxis qAxis = new NumberAxis("Q [m³/s]"); + + plot.setRangeAxis(2, qAxis); + plot.mapDatasetToRangeAxis(1, 2); + } + + + public void doOut(Artifact artifact, String facet, Document attr) { + logger.debug("DurationCurveGenerator.doOut: " + facet); + + if (facet == null || facet.length() == 0) { + logger.error("No facet given. Cannot create dataset."); + return; + } + + if (facet.equals(DURATION_CURVE_W)) { + doWOut(getDurationCurveData(artifact)); + } + else if (facet.equals(DURATION_CURVE_Q)) { + doQOut(getDurationCurveData(artifact)); + } + else { + logger.warn("Unknown facet name: " + facet); + return; + } + } + + + /** + * Creates the series for a duration curve's W facet. + * + * @param wqdays The WQDay store that contains the Ws. + */ + protected void doWOut(WQDay wqdays) { + logger.debug("DurationCurveGenerator.doWOut"); + + // TODO find the correct series name + XYSeries series = new XYSeries("W-1"); + + int size = wqdays.size(); + for (int i = 0; i < size; i++) { + int day = wqdays.getDay(i); + double w = wqdays.getW(i); + + series.add((double) day, w); + } + + this.w.addSeries(series); + } + + + /** + * Creates the series for a duration curve's Q facet. + * + * @param wqdays The WQDay store that contains the Qs. + */ + protected void doQOut(WQDay wqdays) { + logger.debug("DurationCurveGenerator.doQOut"); + + // TODO find the correct series name + XYSeries series = new XYSeries("Q-1"); + + int size = wqdays.size(); + for (int i = 0; i < size; i++) { + int day = wqdays.getDay(i); + double q = wqdays.getQ(i); + + series.add((double) day, q); + } + + this.q.addSeries(series); + } + + + /** + * Returns the computed data for a duration curve based on the artifact's + * computation method. + * + * @param artifact The WINFO artifact. + * + * @return the computed data for a duration curve's W and Q facet. + */ + protected WQDay getDurationCurveData(Artifact artifact) { + WINFOArtifact winfoArtifact = (WINFOArtifact) artifact; + return winfoArtifact.getDurationCurveData(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,295 @@ +package de.intevation.flys.exports; + +import java.awt.Color; + +import org.apache.log4j.Logger; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.title.TextTitle; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; + +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.WQKms; + + +/** + * An OutGenerator that generates discharge curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class LongitudinalSectionGenerator extends XYChartGenerator { + + /** The logger that is used in this generator.*/ + private static Logger logger = + Logger.getLogger(LongitudinalSectionGenerator.class); + + + public static final String LONGITUDINAL_SECTION_W = + "longitudinal_section.w"; + + public static final String LONGITUDINAL_SECTION_Q = + "longitudinal_section.q"; + + public static final String I18N_CHART_TITLE = + "chart.longitudinal.section.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.longitudinal.section.subtitle"; + + public static final String I18N_XAXIS_LABEL = + "chart.longitudinal.section.xaxis.label"; + + public static final String I18N_YAXIS_LABEL = + "chart.longitudinal.section.yaxis.label"; + + public static final String I18N_2YAXIS_LABEL = + "chart.longitudinal.section.yaxis.second.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = "W-Längsschnitt"; + public static final String I18N_XAXIS_LABEL_DEFAULT = "km"; + public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; + public static final String I18N_2YAXIS_LABEL_DEFAULT = "Q [m³/s]"; + + /** The storage for the W series to be drawn in this chart.*/ + protected XYSeriesCollection w; + + /** The storage for the Q series to be drawn in this chart.*/ + protected XYSeriesCollection q; + + + public LongitudinalSectionGenerator() { + super(); + + this.w = new XYSeriesCollection(); + this.q = new XYSeriesCollection(); + } + + + protected String getChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + + @Override + protected void addSubtitles(JFreeChart chart) { + double[] dist = getRange(); + + Object[] args = new Object[] { + getRiverName(), + dist[0], + dist[1] + }; + + String subtitle = msg(I18N_CHART_SUBTITLE, "", args); + chart.addSubtitle(new TextTitle(subtitle)); + } + + + protected String getXAxisLabel() { + return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); + } + + + protected String getYAxisLabel() { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + + protected void addDatasets(JFreeChart chart) { + XYPlot plot = (XYPlot) chart.getPlot(); + + plot.setDataset(0, w); + plot.setDataset(1, q); + } + + + protected void adjustPlot(XYPlot plot) { + super.adjustPlot(plot); + + // TODO REMOVE THIS CODE, IF WE HAVE INTRODUCED THEMES! + XYLineAndShapeRenderer rw = (XYLineAndShapeRenderer) + plot.getRendererForDataset(w); + + XYLineAndShapeRenderer rq = null; + try { + rq = (XYLineAndShapeRenderer) rw.clone(); + } + catch (Exception e) { + logger.error(e, e); + } + + int wNum = w.getSeriesCount(); + int qNum = q.getSeriesCount(); + + for (int i = 0; i < wNum; i++) { + rw.setSeriesPaint(i, Color.BLUE); + } + + for (int i = 0; i < qNum; i++) { + rq.setSeriesPaint(i, Color.GREEN); + } + + plot.setRenderer(0, rw); + plot.setRenderer(1, rq); + } + + + protected void adjustAxes(XYPlot plot) { + super.adjustAxes(plot); + + NumberAxis qAxis = new NumberAxis( + msg(I18N_2YAXIS_LABEL, I18N_2YAXIS_LABEL_DEFAULT)); + + plot.setRangeAxis(2, qAxis); + plot.mapDatasetToRangeAxis(1, 2); + + invertXAxis(plot.getDomainAxis()); + } + + + /** + * This method inverts the x-axis based on the kilometer information of the + * selected river. If the head of the river is at kilometer 0, the axis is + * not inverted, otherwise it is. + * + * @param xaxis The domain axis. + */ + protected void invertXAxis(ValueAxis xaxis) { + FLYSArtifact artifact = (FLYSArtifact) master; + River river = artifact.getRiver(); + + boolean up = river.getKmUp(); + + if (up) { + logger.info("Invert X-Axis."); + xaxis.setInverted(true); + } + } + + + public void doOut(Artifact artifact, String facet, Document attr) { + logger.debug("LongitudinalSectionGenerator.doOut: " + facet); + + if (facet == null) { + logger.error("No facet name for doOut(). No output generated!"); + return; + } + + if (facet.equals(LONGITUDINAL_SECTION_W)) { + doWOut(getWaterlevelData(artifact)); + } + else if (facet.equals(LONGITUDINAL_SECTION_Q)) { + doQOut(getWaterlevelData(artifact)); + } + else { + logger.warn("Unknown facet name: " + facet); + return; + } + } + + + /** + * Returns the waterlevel data computed by the WINFOArtifact. + * + * @param artifact The WINFOArtifact. + * + * @return the computed waterlevel data. + */ + protected WQKms[] getWaterlevelData(Artifact artifact) { + WINFOArtifact winfoArtifact = (WINFOArtifact) artifact; + WQKms[] wqkms = winfoArtifact.getWaterlevelData(); + + logger.debug("Got " + wqkms.length + " WQKms objects."); + + return wqkms; + } + + + /** + * Process the output for W facets in a longitudinal section curve. + * + * @param wqkms An array of WQKms values. + */ + protected void doWOut(WQKms[] wqkms) { + logger.debug("LongitudinalSectionGenerator.doWOut"); + + int idx = 0; + for (WQKms tmp: wqkms) { + XYSeries series = new XYSeries(getSeriesName(tmp, "w", idx++)); + + double[] target = new double[3]; + int size = tmp.size(); + + if (logger.isDebugEnabled()) { + if (tmp.size() > 0) { + logger.debug("Generate series: " + series.getKey()); + logger.debug("Start km: " + tmp.getKms(0)); + logger.debug("End km: " + tmp.getKms(size-1)); + logger.debug("Values : " + size); + } + } + + for (int i = 0; i < size; i++) { + target = tmp.get(i, target); + + series.add(target[2], target[0]); + } + + w.addSeries(series); + } + } + + + /** + * Process the output for Q facets in a longitudinal section curve. + * + * @param wqkms An array of WQKms values. + */ + protected void doQOut(WQKms[] wqkms) { + logger.debug("LongitudinalSectionGenerator.doQOut"); + + int idx = 0; + for (WQKms tmp: wqkms) { + XYSeries series = new XYSeries(getSeriesName(tmp, "Q", idx++)); + + double[] target = new double[3]; + int size = tmp.size(); + + if (logger.isDebugEnabled()) { + if (tmp.size() > 0) { + logger.debug("Generate series: " + series.getKey()); + logger.debug("Start km: " + tmp.getKms(0)); + logger.debug("End km: " + tmp.getKms(size-1)); + logger.debug("Values : " + size); + } + } + + for (int i = 0; i < size; i++) { + target = tmp.get(i, target); + + //logger.debug("++ Q Tuple: " + target[2] + " -> " + target[1]); + series.add(target[2], target[1]); + } + + q.addSeries(series); + } + } + + + protected String getSeriesName(WQKms wqkms, String prefix, int idx) { + return prefix + "-" + idx; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/OutGenerator.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,56 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + + +/** + * An OutGenerator is used to create a collected outputs of a list of Artifacts. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface OutGenerator { + + /** + * Initializes the OutGenerator with meta information which are necessary + * for the output generation. + * + * @param request The incomding request document. + * @param out The output stream. + * @param context The CallContext that provides further information and + * objects used for the output generation. + */ + void init(Document request, OutputStream out, CallContext context); + + /** + * This method is used to tell the OutGenerator which artifact is the master + * artifact which is used for special operations. + * + * @param master The master artifact. + */ + void setMasterArtifact(Artifact master); + + /** + * Creates the output of an Artifact and appends that single output to the + * total output. + * + * @param artifact The artifact that provides information and data for the + * single output. + * @param attr A document that might contain some attributes used while + * producing the output. + */ + void doOut(Artifact artifact, String facet, Document attr); + + /** + * Writes the collected output of all artifacts specified in the + * <i>request</i> (see init()) document to the OutputStream <i>out</i> (see + * init()). + */ + void generate() throws IOException; +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/WaterlevelExporter.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,162 @@ +package de.intevation.flys.exports; + +import java.io.OutputStream; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import au.com.bytecode.opencsv.CSVWriter; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.WQKms; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WaterlevelExporter extends AbstractExporter { + + /** The logger used in this exporter.*/ + private static Logger logger = Logger.getLogger(WaterlevelExporter.class); + + + public static final int KM_MIN_DIGITS = 3; + public static final int KM_MAX_DIGITS = 3; + public static final int W_MIN_DIGITS = 0; + public static final int W_MAX_DIGITS = 2; + public static final int Q_MIN_DIGITS = 0; + public static final int Q_MAX_DIGITS = 2; + + + public static final String CSV_KM_HEADER = + "export.waterlevel.csv.header.km"; + + public static final String CSV_W_HEADER = + "export.waterlevel.csv.header.w"; + + public static final String CSV_Q_HEADER = + "export.waterlevel.csv.header.q"; + + public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km"; + public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; + public static final String DEFAULT_CSV_Q_HEADER = "Q [m³/s]"; + + + /** The storage that contains all WQKms objects for the different facets.*/ + protected List<WQKms[]> data; + + + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("WaterlevelExporter.init"); + + super.init(request, out, context); + + this.data = new ArrayList<WQKms[]>(); + } + + + protected void addData(Artifact artifact) { + data.add(getWaterlevelData(artifact)); + } + + + /** + * Returns the waterlevel data computed by the WINFOArtifact. + * + * @param artifact The WINFOArtifact. + * + * @return the computed waterlevel data. + */ + protected WQKms[] getWaterlevelData(Artifact artifact) { + WINFOArtifact winfoArtifact = (WINFOArtifact) artifact; + WQKms[] wqkms = winfoArtifact.getWaterlevelData(); + + logger.debug("Got " + wqkms.length + " WQKms objects."); + + return wqkms; + } + + + protected void writeCSVData(CSVWriter writer) { + logger.info("WaterlevelExporter.writeData"); + + writeCSVHeader(writer); + + for (WQKms[] tmp: data) { + for (WQKms wqkms: tmp) { + wQKms2CSV(writer, wqkms); + } + } + } + + + protected void writeCSVHeader(CSVWriter writer) { + logger.info("WaterlevelExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), + msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), + msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER) + }); + } + + + protected void wQKms2CSV(CSVWriter writer, WQKms wqkms) { + logger.debug("WaterlevelExporter.wQKms2CSV"); + + NumberFormat kmf = getKmFormatter(); + NumberFormat wf = getWFormatter(); + NumberFormat qf = getQFormatter(); + + int size = wqkms.size(); + double[] result = new double[3]; + + for (int i = 0; i < size; i ++) { + result = wqkms.get(i, result); + + writer.writeNext(new String[] { + kmf.format(result[2]), + wf.format(result[0]), + qf.format(result[1]) + }); + } + } + + + /** + * Returns the number formatter for kilometer values. + * + * @return the number formatter for kilometer values. + */ + protected NumberFormat getKmFormatter() { + return getFormatter(KM_MIN_DIGITS, KM_MAX_DIGITS); + } + + + /** + * Returns the number formatter for W values. + * + * @return the number formatter for W values. + */ + protected NumberFormat getWFormatter() { + return getFormatter(W_MIN_DIGITS, W_MAX_DIGITS); + } + + + /** + * Returns the number formatter for Q values. + * + * @return the number formatter for Q values. + */ + protected NumberFormat getQFormatter() { + return getFormatter(Q_MIN_DIGITS, Q_MAX_DIGITS); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,135 @@ +package de.intevation.flys.exports; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Stroke; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; + +import de.intevation.flys.exports.ChartExportHelper; + + +/** + * An abstract base class for creating XY charts. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class XYChartGenerator extends ChartGenerator { + + /** The logger that is used in this generator.*/ + private static Logger logger = Logger.getLogger(ChartGenerator.class); + + + public static final Color DEFAULT_GRID_COLOR = Color.GRAY; + public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; + + + /** + * Returns the title of a chart. + * + * @return the title of a chart. + */ + protected abstract String getChartTitle(); + + /** + * Returns the X-Axis label of a chart. + * + * @return the X-Axis label of a chart. + */ + protected abstract String getXAxisLabel(); + + /** + * Returns the Y-Axis label of a chart. + * + * @return the Y-Axis label of a chart. + */ + protected abstract String getYAxisLabel(); + + /** + * This method is called to add all datasets of a concrete XYChartGenerator + * to the JFreeChart. + * + * @param chart The JFreeChart object. + */ + protected abstract void addDatasets(JFreeChart chart); + + + public void generate() + throws IOException + { + logger.debug("XYChartGenerator.generate"); + + JFreeChart chart = ChartFactory.createXYLineChart( + getChartTitle(), + getXAxisLabel(), + getYAxisLabel(), + null, + PlotOrientation.VERTICAL, + true, + false, + false); + + chart.setBackgroundPaint(Color.WHITE); + chart.getPlot().setBackgroundPaint(Color.WHITE); + + XYPlot plot = (XYPlot) chart.getPlot(); + + addDatasets(chart); + addSubtitles(chart); + adjustPlot(plot); + adjustAxes(plot); + + int[] size = getSize(); + + ChartExportHelper.exportImage( + out, + chart, + "png", + size[0], size[1]); + } + + + /** + * Adjusts the axes of a plot. + * + * @param plot The XYPlot of the chart. + */ + protected void adjustAxes(XYPlot plot) { + NumberAxis yAxis = (NumberAxis) plot.getRangeAxis(); + + yAxis.setAutoRangeIncludesZero(false); + } + + + protected void adjustPlot(XYPlot plot) { + Stroke gridStroke = new BasicStroke( + DEFAULT_GRID_LINE_WIDTH, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, + 3.0f, + new float[] { 3.0f }, + 0.0f); + + plot.setDomainGridlineStroke(gridStroke); + plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR); + plot.setDomainGridlinesVisible(true); + + plot.setRangeGridlineStroke(gridStroke); + plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); + plot.setRangeGridlinesVisible(true); + } + + + protected void addSubtitles(JFreeChart chart) { + // override this method in subclasses that need subtitles + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/DefaultTheme.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,167 @@ +package de.intevation.flys.themes; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultTheme implements Theme { + + /** The name of the theme.*/ + protected String name; + + /** The description of the theme.*/ + protected String description; + + + /** The map storing the fields of this theme.*/ + protected Map<String, ThemeField> fields; + + /** The map storing the attributes of this theme.*/ + protected Map<String, String> attr; + + + /** + * Initializes the components of this Theme. + */ + public DefaultTheme(String name, String description) { + this.name = name; + this.description = description; + this.fields = new HashMap<String, ThemeField>(); + this.attr = new HashMap<String, String>(); + } + + + public void init(Node config) { + } + + + public String getName() { + return name; + } + + + public String getDescription() { + return description; + } + + + public void addAttribute(String name, String value) { + if (name != null && value != null) { + attr.put(name, value); + } + } + + + public String getAttribute(String name) { + return attr.get(name); + } + + + public void addField(String name, ThemeField field) { + if (name != null && field != null) { + fields.put(name, field); + } + } + + + public void setFieldValue(String name, Object value) { + if (name != null && value != null) { + ThemeField field = fields.get(name); + + if (field != null) { + field.setValue(value); + } + } + } + + + public ThemeField getField(String name) { + return fields.get(name); + } + + + public String getFieldType(String name) { + ThemeField field = fields.get(name); + + return field != null ? field.getType() : null; + } + + + public Object getFieldValue(String name) { + ThemeField field = fields.get(name); + + return field != null ? field.getValue() : null; + } + + + public Document toXML() { + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator(doc, null, null); + + Element theme = cr.create("theme"); + + appendAttributes(cr, theme); + appendFields(cr, theme); + + doc.appendChild(theme); + + return doc; + } + + + /** + * Appends the attributes configured for this theme. + * + * @param cr The ElementCreator. + * @param theme The document root element. + */ + protected void appendAttributes(ElementCreator cr, Element theme) { + Iterator<String> iter = attr.keySet().iterator(); + + while (iter.hasNext()) { + String key = iter.next(); + String val = getAttribute(key); + + if (key == null || val == null) { + continue; + } + + cr.addAttr(theme, key, val); + } + } + + + /** + * Appends the fields configured for this theme. + * + * @param cr The ElementCreator. + * @param theme The document root element. + */ + protected void appendFields(ElementCreator cr, Element theme) { + Iterator<String> iter = fields.keySet().iterator(); + + while (iter.hasNext()) { + String name = iter.next(); + + ThemeField field = getField(name); + + Document doc = field.toXML(); + Node root = doc.getFirstChild(); + + theme.appendChild(theme.getOwnerDocument().importNode(root, true)); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/DefaultThemeField.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,85 @@ +package de.intevation.flys.themes; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultThemeField implements ThemeField { + + protected String name; + + protected Map<String, Object> attr; + + + public DefaultThemeField(String name) { + this.name = name; + this.attr = new HashMap<String, Object>(); + } + + + public String getName() { + return name; + } + + + public String getType() { + return (String) getAttribute("type"); + } + + + public Object getValue() { + return getAttribute("value"); + } + + + public void setValue(Object value) { + setAttribute("value", value); + } + + + public Object getAttribute(String name) { + return attr.get(name); + } + + + public void setAttribute(String name, Object value) { + if (name == null || value == null) { + return; + } + + attr.put(name, value); + } + + + public Document toXML() { + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator(doc, null, null); + + Element field = cr.create("field"); + + Iterator<String> iter = attr.keySet().iterator(); + + while (iter.hasNext()) { + String name = iter.next(); + String value = (String) getAttribute(name); + + cr.addAttr(field, name, value); + } + + doc.appendChild(field); + + return doc; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/Theme.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,110 @@ +package de.intevation.flys.themes; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface Theme { + + /** + * Method to initialize the theme. + * + * @param config The configuration node. + */ + void init(Node config); + + + /** + * Returns the name of the theme. + * + * @return the name of the theme. + */ + String getName(); + + + /** + * Returns the description of the theme. + * + * @return the description of the theme. + */ + String getDescription(); + + + /** + * Adds a new attribute. + * + * @param name The name of the attribute. + * @param value The value of the attribute. + */ + void addAttribute(String name, String value); + + + /** + * Returns the value of a specific attribute. + * + * @param name the name of the attribute. + * + * @return the value of the attribute <i>name</i>. + */ + String getAttribute(String name); + + + /** + * Adds a new field to the theme. + * + * @param name The name of the field. + * @param field The field. + */ + void addField(String name, ThemeField field); + + + /** + * Sets the value of an field. + * + * @param name The name of the field. + * @param value The new value of the field. + */ + void setFieldValue(String name, Object value); + + + /** + * Returns the field specified by name. + * + * @param name The name of the desired field. + * + * @return an field. + */ + ThemeField getField(String name); + + + /** + * Returns the typename of a field. + * + * @param name the name of the field. + * + * @return the typename of a field. + */ + String getFieldType(String name); + + + /** + * Returns the value of a field. + * + * @param name The name of the field. + * + * @return the value of a field. + */ + Object getFieldValue(String name); + + + /** + * Dumps the theme to XML. + * + * @return a document. + */ + Document toXML(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeFactory.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,218 @@ +package de.intevation.flys.themes; + +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.context.FLYSContext; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ThemeFactory { + + private static Logger logger = Logger.getLogger(ThemeFactory.class); + + private ThemeFactory() { + } + + + /** + * Creates a new theme from <i>config</i>. + * + * @param themeCfg The theme config document that is used to fetch parent + * themes. + * @param config The theme config node. + * + * @return a new theme. + */ + public static Theme createTheme(Document themeCfg, Node config) { + String name = getName(config); + String desc = getDescription(config); + + logger.debug("Create new theme: " + name); + + Theme theme = new DefaultTheme(name, desc); + + parseInherits(themeCfg, config, theme); + parseFields(config, theme); + parseAttrs(config, theme); + + return theme; + } + + + /** + * Returns the theme for a specified output type and facet. + * + * @param c The FLYSContext that stores the mappings and themes. + * @param name The name of the mapping. + * + * @return a theme. + */ + public static Theme getTheme(FLYSContext c, String name) { + if (c == null || name == null) { + logger.warn("Cannot search for theme."); + return null; + } + + Map<String, String> map = (Map<String, String>) + c.get(FLYSContext.THEME_MAPPING); + + Map<String, Theme> t = (Map<String, Theme>) + c.get(FLYSContext.THEMES); + + if (map == null || map.size() == 0 || t == null || t.size() == 0) { + logger.warn("No mappings or themes found. Cannot retrieve theme!"); + return null; + } + + String themeName = map.get(name); + + if (themeName == null) { + logger.warn("No theme found for mapping: " + name); + } + + return t.get(themeName); + } + + + protected static String getName(Node config) { + return (String) XMLUtils.xpath(config, "@name", XPathConstants.STRING); + } + + + protected static String getDescription(Node config) { + return (String) XMLUtils.xpath(config, "@desc", XPathConstants.STRING); + } + + + protected static void parseInherits(Document themeCfg, Node cfg, Theme t) { + logger.debug("ThemeFactory.parseInherits"); + + NodeList inherits = (NodeList) XMLUtils.xpath( + cfg, "inherits/inherit", XPathConstants.NODESET); + + int num = inherits != null ? inherits.getLength() : 0; + + if (num == 0) { + logger.debug("Theme does not inherit from other themes."); + return; + } + + logger.debug("Theme inherits from " + num + " other themes."); + + for (int i = 0; i < num; i++) { + Node inherit = inherits.item(i); + String from = (String) XMLUtils.xpath( + inherit, "@from", XPathConstants.STRING); + + Node tmp = getThemeNode(themeCfg, from); + + parseInherits(themeCfg, tmp, t); + parseFields(tmp, t); + } + } + + + protected static Node getThemeNode(Document themeCfg, String name) { + if (name == null) { + logger.warn("Cannot search theme config without name!"); + return null; + } + + logger.debug("Search for theme: " + name); + + String xpath = "/themes/theme[@name='" + name + "']"; + + return (Node) XMLUtils.xpath(themeCfg, xpath, XPathConstants.NODE); + } + + + protected static void parseFields(Node config, Theme theme) { + if (config == null || theme == null) { + logger.warn("Parsing fields without node or theme is senseless!"); + return; + } + + NodeList fields = (NodeList) XMLUtils.xpath( + config, "fields/field", XPathConstants.NODESET); + + int num = fields != null ? fields.getLength() : 0; + + logger.debug("Found " + num + " own fields in this theme."); + + if (num == 0) { + logger.debug("Theme has no own fields."); + return; + } + + for (int i = 0; i < num; i++) { + Node field = fields.item(i); + + addField(theme, field); + } + } + + + protected static void addField(Theme theme, Node field) { + String name = (String) XMLUtils.xpath( + field, "@name", XPathConstants.STRING); + + logger.debug("Add field: " + name); + + NamedNodeMap attrs = field.getAttributes(); + + int num = attrs != null ? attrs.getLength() : 0; + + if (num == 0) { + logger.warn("This field has no attributes."); + return; + } + + ThemeField theField = new DefaultThemeField(name); + + for (int i = 0; i < num; i++) { + Node attr = attrs.item(i); + + String key = attr.getNodeName(); + String value = attr.getNodeValue(); + + theField.setAttribute(key, value); + } + + theme.addField(name, theField); + } + + + protected static void parseAttrs(Node config, Theme theme) { + NamedNodeMap attrs = config.getAttributes(); + + int num = attrs != null ? attrs.getLength() : 0; + + if (num == 0) { + logger.debug("Theme has no attributes set."); + return; + } + + for (int i = 0; i < num; i++) { + Node attr = attrs.item(i); + + String name = attr.getNodeName(); + String value = attr.getNodeValue(); + + theme.addAttribute(name, value); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeField.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,58 @@ +package de.intevation.flys.themes; + +import org.w3c.dom.Document; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface ThemeField { + + /** + * Returns the name of this field. + * + * @return the name of this field. + */ + String getName(); + + /** + * Returns the type of this field. + * + * @return the type of this field. + */ + String getType(); + + + /** + * Returns the value of this field. + * + * @return the value of this field. + */ + Object getValue(); + + + /** + * Changes the value of this field. + * + * @param value The new value. + */ + void setValue(Object value); + + + /** + * Sets the value of an attribute. + * + * @param name The name of an attribute. + * @param value The value of an attribute. + */ + void setAttribute(String name, Object value); + + + /** + * Dumps the field to XML. + * + * @return a document. + */ + Document toXML(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/messages.properties Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,45 @@ +state.winfo.river = River +state.winfo.calculation_mode = Calculation Mode +state.winfo.location_distance = Location or distance selection +state.winfo.wq = Input for W/Q data +state.winfo.wq_adapted = Input for W/Q data +state.winfo.location = Choose the location + +calc.surface.curve = Water Level/Surface Curve +calc.flood.map = Flood Plain +calc.discharge.curve = State Discharge Curve/Stage Discharge Relation +calc.duration.curve = Duration Curve +calc.discharge.longitudinal.section = TODO (W bei...) + +river = River +calculation_mode = Calculation Mode +ld_locations = Location(s) + +chart.longitudinal.section.title = W-Longitudinal Section +chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} +chart.longitudinal.section.xaxis.label = km +chart.longitudinal.section.yaxis.label = W [NN + m] +chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s] +chart.discharge.curve.title = Discharge Curve +chart.discharge.curve.xaxis.label = Q [m\u00b3/s] +chart.discharge.curve.yaxis.label = W [cm] +chart.computed.discharge.curve.title = Discharge Curve +chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###} +chart.computed.discharge.curve.yaxis.label = W [NN + m] +chart.duration.curve.title = Duration Curve +chart.duration.curve.subtitle = {0}-km: {1,number,#.###} +chart.duration.curve.xaxis.label = Duration of Non-Exceedence [Days] +chart.duration.curve.yaxis.label = W [NN + m] + +export.waterlevel.csv.header.km = River-Km +export.waterlevel.csv.header.w = W [NN + m] +export.waterlevel.csv.header.q = Q [m\u00b3/s] +export.computed.discharge.curve.csv.header.w = W [NN + m] +export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s] +export.duration.curve.csv.header.duration = D [Days] +export.duration.curve.csv.header.w = W [NN + m] +export.duration.curve.csv.header.q = Q [m\u00b3/s] +export.discharge.longitudinal.section.csv.header.km = River-Km +export.discharge.longitudinal.section.csv.header.w = W [NN + m] +export.discharge.longitudinal.section.csv.header.cw = W corr. +export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/messages_de.properties Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,45 @@ +state.winfo.river = Gew\u00e4sser +state.winfo.calculation_mode = Berechnungsart +state.winfo.location_distance = Wahl des Berechnungsortes/strecke +state.winfo.wq = Eingabe f\u00fcr W/Q Daten +state.winfo.wq_adapted = Eingabe f\u00fcr W/Q Daten +state.winfo.location = Wahl des Berechnungsortes + +calc.surface.curve = Wasserstand/Wasserspiegellage +calc.flood.map = \u00dcberschwemmungsfl\u00e4che +calc.discharge.curve = Abflusskurve/Abflusstafel +calc.duration.curve = Dauerlinie +calc.discharge.longitudinal.section = W bei ungleichm\u00e4\u00dfigem Abflussl\u00e4ngsschnitt + +river = Fluss +calculation_mode = Berechnungsart +ld_locations = Ort(e) + +chart.longitudinal.section.title = W-L\u00e4ngsschnitt +chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###} +chart.longitudinal.section.xaxis.label = km +chart.longitudinal.section.yaxis.label = W [NN + m] +chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s] +chart.discharge.curve.title = Abflusskurve +chart.discharge.curve.xaxis.label = Q [m\u00b3/s] +chart.discharge.curve.yaxis.label = W [cm] +chart.computed.discharge.curve.title = Abflusskurve +chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###} +chart.computed.discharge.curve.yaxis.label = W [NN + m] +chart.duration.curve.title = Dauerlinie +chart.duration.curve.subtitle = {0}-km: {1,number,#.###} +chart.duration.curve.xaxis.label = Unterschreitungsdauer [Tage] +chart.duration.curve.yaxis.label = W [NN + m] + +export.waterlevel.csv.header.km = Fluss-Km +export.waterlevel.csv.header.w = W [NN + m] +export.waterlevel.csv.header.q = Q [m\u00b3/s] +export.computed.discharge.curve.csv.header.w = W [NN + m] +export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s] +export.duration.curve.csv.header.duration = D [Tagen] +export.duration.curve.csv.header.w = W [NN + m] +export.duration.curve.csv.header.q = Q [m\u00b3/s] +export.discharge.longitudinal.section.csv.header.km = Fluss-Km +export.discharge.longitudinal.section.csv.header.w = W [NN + m] +export.discharge.longitudinal.section.csv.header.cw = W korr. +export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/messages_de_DE.properties Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,45 @@ +state.winfo.river = Gew\u00e4sser +state.winfo.calculation_mode = Berechnungsart +state.winfo.location_distance = Wahl des Berechnungsortes/strecke +state.winfo.wq = Eingabe f\u00fcr W/Q Daten +state.winfo.wq_adapted = Eingabe f\u00fcr W/Q Daten +state.winfo.location = Wahl des Berechnungsortes + +calc.surface.curve = Wasserstand/Wasserspiegellage +calc.flood.map = \u00dcberschwemmungsfl\u00e4che +calc.discharge.curve = Abflusskurve/Abflusstafel +calc.duration.curve = Dauerlinie +calc.discharge.longitudinal.section = W bei ungleichm\u00e4\u00dfigem Abflussl\u00e4ngsschnitt + +river = Fluss +calculation_mode = Berechnungsart +ld_locations = Ort(e) + +chart.longitudinal.section.title = W-L\u00e4ngsschnitt +chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###} +chart.longitudinal.section.xaxis.label = km +chart.longitudinal.section.yaxis.label = W [NN + m] +chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s] +chart.discharge.curve.title = Abflusskurve +chart.discharge.curve.xaxis.label = Q [m\u00b3/s] +chart.discharge.curve.yaxis.label = W [cm] +chart.computed.discharge.curve.title = Abflusskurve +chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###} +chart.computed.discharge.curve.yaxis.label = W [NN + m] +chart.duration.curve.title = Dauerlinie +chart.duration.curve.subtitle = {0}-km: {1,number,#.###} +chart.duration.curve.xaxis.label = Unterschreitungsdauer [Tage] +chart.duration.curve.yaxis.label = W [NN + m] + +export.waterlevel.csv.header.km = Fluss-Km +export.waterlevel.csv.header.w = W [NN + m] +export.waterlevel.csv.header.q = Q [m\u00b3/s] +export.computed.discharge.curve.csv.header.w = W [NN + m] +export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s] +export.duration.curve.csv.header.duration = D [Tagen] +export.duration.curve.csv.header.w = W [NN + m] +export.duration.curve.csv.header.q = Q [m\u00b3/s] +export.discharge.longitudinal.section.csv.header.km = Fluss-Km +export.discharge.longitudinal.section.csv.header.w = W [NN + m] +export.discharge.longitudinal.section.csv.header.cw = W korr. +export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/messages_en.properties Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,45 @@ +state.winfo.river = River +state.winfo.calculation_mode = Calculation Mode +state.winfo.location_distance = Location or distance selection +state.winfo.wq = Input for W/Q data +state.winfo.wq_adapted = Input for W/Q data +state.winfo.location = Choose the location + +calc.surface.curve = Water Level/Surface Curve +calc.flood.map = Flood Plain +calc.discharge.curve = State Discharge Curve/Stage Discharge Relation +calc.duration.curve = Duration Curve +calc.discharge.longitudinal.section = TODO (W bei...) + +river = River +calculation_mode = Calculation Mode +ld_locations = Location(s) + +chart.longitudinal.section.title = W-Longitudinal Section +chart.longitudinal.section.subtitle = Range: {0}-km {1,number,double} - {2,number,double} +chart.longitudinal.section.xaxis.label = km +chart.longitudinal.section.yaxis.label = W [NN + m] +chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s] +chart.discharge.curve.title = Discharge Curve +chart.discharge.curve.xaxis.label = Q [m\u00b3/s] +chart.discharge.curve.yaxis.label = W [cm] +chart.computed.discharge.curve.title = Discharge Curve +chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###} +chart.computed.discharge.curve.yaxis.label = W [NN + m] +chart.duration.curve.title = Duration Curve +chart.duration.curve.subtitle = {0}-km: {1,number,#.###} +chart.duration.curve.xaxis.label = Duration of Non-Exceedence [Days] +chart.duration.curve.yaxis.label = W [NN + m] + +export.waterlevel.csv.header.km = River-Km +export.waterlevel.csv.header.w = W [NN + m] +export.waterlevel.csv.header.q = Q [m\u00b3/s] +export.computed.discharge.curve.csv.header.w = W [NN + m] +export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s] +export.duration.curve.csv.header.duration = D [Days] +export.duration.curve.csv.header.w = W [NN + m] +export.duration.curve.csv.header.q = Q [m\u00b3/s] +export.discharge.longitudinal.section.csv.header.km = River-Km +export.discharge.longitudinal.section.csv.header.w = W [NN + m] +export.discharge.longitudinal.section.csv.header.cw = W corr. +export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/metadata/template.xml Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<dc:template xmlns:dc="http://www.intevation.org/2011/Datacage"> +<datacage> + <dc:context> + <dc:statement> + SELECT id AS river_id, name as river_name FROM rivers + WHERE name ILIKE ${river} + </dc:statement> + <dc:elements> + <river> + <dc:attribute name="name" value="${river_name}"/> + <dc:attribute name="river-id" value="${river_id}"/> + <discharge-table-nn> + <discharge-table-gauge> + <dc:context> + <dc:statement> + SELECT id AS gauge_id, + name AS gauge_name + FROM gauges WHERE river_id = ${river_id} + </dc:statement> + <dc:elements> + <gauge> + <dc:attribute name="name" value="${gauge_name}"/> + <dc:attribute name="gauge-id" value="${gauge_id}"/> + <dc:context> + <dc:statement> + SELECT description AS gauge_desc, + d.id AS discharge_id, + ti.start_time AS g_start, + ti.stop_time AS g_stop + FROM discharge_tables d JOIN time_intervals ti + ON d.time_interval_id = ti.id + WHERE d.gauge_id = ${gauge_id} AND d.kind = 1 + </dc:statement> + <dc:elements> + <historical> + <dc:attribute name="name" value="${gauge_desc}"/> + <dc:attribute name="from" value="${g_start}"/> + <dc:attribute name="to" value="${g_stop}"/> + <dc:attribute name="discharge-id" value="${discharge_id}"/></historical> + </dc:elements> + </dc:context> + </gauge> + </dc:elements> + </dc:context> + </discharge-table-gauge> + <fixations> + <dc:attribute name="id" value="fixations-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS fix_id, + description AS fix_description + FROM wsts WHERE kind = 2 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <fixation> + <dc:attribute name="name" value="${fix_description}"/> + <dc:attribute name="fix-id" value="${fix_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS fix_column_id, + name AS fix_column_name + FROM wst_columns WHERE wst_id = ${fix_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${fix_column_name}"/> + <dc:attribute name="column-id" value="${fix_column_id}"/></column> + </dc:elements> + </dc:context> + </columns> + </fixation> + </dc:elements> + </dc:context> + </fixations> + <flood-protections> + <dc:attribute name="id" value="flood-protections-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS prot_id, + description AS prot_description + FROM wsts WHERE kind = 5 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <flood-protection> + <dc:attribute name="name" value="${prot_description}"/> + <dc:attribute name="prot-id" value="${prot_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS prot_column_id, + name AS prot_column_name + FROM wst_columns WHERE wst_id = ${prot_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${prot_column_name}"/> + <dc:attribute name="column-id" value="${prot_column_id}"/></column> + </dc:elements> + </dc:context> + </columns> + </flood-protection> + </dc:elements> + </dc:context> + </flood-protections> + <flood-water-marks> + <dc:attribute name="id" value="flood-water-marks-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS fw_id, + description AS fw_description + FROM wsts WHERE kind = 4 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <flood-water-mark> + <dc:attribute name="name" value="${fw_description}"/> + <dc:attribute name="fw-id" value="${fw_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS fw_column_id, + name AS fw_column_name + FROM wst_columns WHERE wst_id = ${fw_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${fw_column_name}"/> + <dc:attribute name="column-id" value="${fw_column_id}"/></column> + </dc:elements> + </dc:context> + </columns> + </flood-water-mark> + </dc:elements> + </dc:context> + </flood-water-marks> + <water-levels> + <dc:attribute name="id" value="water-levels-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS wl_id, + description AS wl_description + FROM wsts WHERE kind = 0 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <water-level> + <dc:attribute name="name" value="${wl_description}"/> + <dc:attribute name="wl-id" value="${wl_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS wl_column_id, + name AS wl_column_name + FROM wst_columns WHERE wst_id = ${wl_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${wl_column_name}"/> + <dc:attribute name="column-id" value="${wl_column_id}"/></column> + </dc:elements> + </dc:context> + </columns> + </water-level> + </dc:elements> + </dc:context> + </water-levels> + <extra-longitudinal-sections> + <dc:attribute name="id" value="extra-longitudinal-sections-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS els_id, + description AS els_description + FROM wsts WHERE kind = 1 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <extra-longitudinal-section> + <dc:attribute name="name" value="${els_description}"/> + <dc:attribute name="els-id" value="${els_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS els_column_id, + name AS els_column_name + FROM wst_columns WHERE wst_id = ${els_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${els_column_name}"/> + <dc:attribute name="column-id" value="${els_column_id}"/></column> + </dc:elements> + </dc:context> + </columns> + </extra-longitudinal-section> + </dc:elements> + </dc:context> + </extra-longitudinal-sections> + </discharge-table-nn> + <longitudinal-section> + <fixations><dc:attribute name="ref" value="fixations-${river_id}"/></fixations> + <flood-protections><dc:attribute name="ref" value="flood-protections-${river_id}"/></flood-protections> + <flood-water-marks><dc:attribute name="ref" value="flood-water-marks-${river_id}"/></flood-water-marks> + <water-levels><dc:attribute name="ref" value="water-levels-${river_id}"/></water-levels> + <extra-longitudinal-sections><dc:attribute name="ref" value="extra-longitudinal-sections-${river_id}"/></extra-longitudinal-sections> + </longitudinal-section> + </river> + </dc:elements> + </dc:context> +</datacage> +</dc:template> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/test/java/de/intevation/flys/artifacts/AppTest.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,38 @@ +package de.intevation.flys.artifacts; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +}