comparison artifacts/src/main/java/org/dive4elements/river/artifacts/states/DefaultState.java @ 9277:2323d005f9a5

compile error fix
author gernotbelger
date Fri, 20 Jul 2018 10:39:02 +0200
parents 7d1a32a543cb
children
comparison
equal deleted inserted replaced
9276:3141f0e7314e 9277:2323d005f9a5
7 */ 7 */
8 8
9 package org.dive4elements.river.artifacts.states; 9 package org.dive4elements.river.artifacts.states;
10 10
11 import java.text.NumberFormat; 11 import java.text.NumberFormat;
12 import java.util.List;
12 import java.util.Locale; 13 import java.util.Locale;
13 import java.util.Map; 14 import java.util.Map;
14 import java.util.List;
15 15
16 import org.apache.log4j.Logger; 16 import org.apache.log4j.Logger;
17 17 import org.dive4elements.artifactdatabase.ProtocolUtils;
18 import org.w3c.dom.Document; 18 import org.dive4elements.artifactdatabase.data.StateData;
19 import org.w3c.dom.Element; 19 import org.dive4elements.artifactdatabase.state.AbstractState;
20 import org.w3c.dom.Node; 20 import org.dive4elements.artifactdatabase.state.Facet;
21
22 import org.dive4elements.artifacts.Artifact; 21 import org.dive4elements.artifacts.Artifact;
23 import org.dive4elements.artifacts.ArtifactNamespaceContext; 22 import org.dive4elements.artifacts.ArtifactNamespaceContext;
24 import org.dive4elements.artifacts.CallContext; 23 import org.dive4elements.artifacts.CallContext;
25 import org.dive4elements.artifacts.CallMeta; 24 import org.dive4elements.artifacts.CallMeta;
26
27 import org.dive4elements.artifacts.common.utils.XMLUtils; 25 import org.dive4elements.artifacts.common.utils.XMLUtils;
28 import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator; 26 import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
29
30 import org.dive4elements.artifactdatabase.ProtocolUtils;
31
32 import org.dive4elements.artifactdatabase.data.StateData;
33
34 import org.dive4elements.artifactdatabase.state.AbstractState;
35 import org.dive4elements.artifactdatabase.state.Facet;
36
37 import org.dive4elements.river.artifacts.D4EArtifact; 27 import org.dive4elements.river.artifacts.D4EArtifact;
38
39 import org.dive4elements.river.artifacts.resources.Resources; 28 import org.dive4elements.river.artifacts.resources.Resources;
40 29 import org.w3c.dom.Document;
30 import org.w3c.dom.Element;
31 import org.w3c.dom.Node;
41 32
42 /** 33 /**
43 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> 34 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
44 */ 35 */
45 public class DefaultState extends AbstractState { 36 public class DefaultState extends AbstractState {
46 37
47 /** The log that is used in this class. */ 38 /** The log that is used in this class. */
48 private static Logger log = Logger.getLogger(DefaultState.class); 39 private static Logger log = Logger.getLogger(DefaultState.class);
49 40
50
51 /** The three possible compute types. */ 41 /** The three possible compute types. */
52 public static enum ComputeType { 42 public static enum ComputeType {
53 FEED, ADVANCE, INIT 43 FEED, ADVANCE, INIT
54 } 44 }
55 45
56 46 protected StateData getData(final D4EArtifact artifact, final String name) {
57 protected StateData getData(D4EArtifact artifact, String name) {
58 return artifact.getData(name); 47 return artifact.getData(name);
59 } 48 }
60 49
61
62 /** 50 /**
63 * Append to a node and return xml description relevant for gui. 51 * Append to a node and return xml description relevant for gui.
64 */ 52 */
65 public Element describeStatic( 53 public Element describeStatic(final Artifact artifact, final Document document, final Node root, final CallContext context, final String uuid) {
66 Artifact artifact, 54 final ElementCreator creator = new ElementCreator(document, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);
67 Document document, 55
68 Node root, 56 final CallMeta meta = context.getMeta();
69 CallContext context,
70 String uuid)
71 {
72 ElementCreator creator = new ElementCreator(
73 document,
74 ArtifactNamespaceContext.NAMESPACE_URI,
75 ArtifactNamespaceContext.NAMESPACE_PREFIX);
76
77 CallMeta meta = context.getMeta();
78 57
79 String helpText = getHelpText(); 58 String helpText = getHelpText();
80 if (helpText != null) { 59 if (helpText != null) {
81 helpText = replaceHelpUrl( 60 helpText = replaceHelpUrl(Resources.getMsg(meta, helpText, helpText));
82 Resources.getMsg(meta, helpText, helpText)); 61 }
83 } 62
84 63 final String label = Resources.getMsg(meta, getID(), getID());
85 String label = Resources.getMsg(meta, getID(), getID()); 64 final Element ui = ProtocolUtils.createArtNode(creator, "state", new String[] { "name", "uiprovider", "label", "helpText" },
86 Element ui = ProtocolUtils.createArtNode( 65 new String[] { getID(), getUIProvider(), label, helpText });
87 creator, "state", 66
88 new String[] { "name", "uiprovider", "label", "helpText"}, 67 final Map<String, StateData> theData = getData();
89 new String[] { getID(), getUIProvider(), label, helpText });
90
91 Map<String, StateData> theData = getData();
92 if (theData == null) { 68 if (theData == null) {
93 return ui; 69 return ui;
94 } 70 }
95 71
96 D4EArtifact flys = (D4EArtifact)artifact; 72 final D4EArtifact flys = (D4EArtifact) artifact;
97 73
98 for (String name: theData.keySet()) { 74 for (final String name : theData.keySet()) {
99 appendStaticData(flys, context, creator, ui, name); 75 appendStaticData(flys, context, creator, ui, name);
100 } 76 }
101 77
102 return ui; 78 return ui;
103 } 79 }
104 80
105 81 protected void appendStaticData(final D4EArtifact flys, final CallContext context, final ElementCreator cr, final Element ui, final String name) {
106 protected void appendStaticData( 82 final StateData data = getData(flys, name);
107 D4EArtifact flys, 83 final String value = (data != null) ? (String) data.getValue() : null;
108 CallContext context,
109 ElementCreator cr,
110 Element ui,
111 String name
112 ) {
113 StateData data = getData(flys, name);
114 String value = (data != null) ? (String) data.getValue() : null;
115 84
116 if (value == null) { 85 if (value == null) {
117 return; 86 return;
118 } 87 }
119 88
120 String type = data.getType(); 89 final String type = data.getType();
121 90
122 if (log.isDebugEnabled()) { 91 if (log.isDebugEnabled()) {
123 log.debug( 92 log.debug("Append element " + type + "'" + name + "' (" + value + ")");
124 "Append element " + type + "'" + 93 }
125 name + "' (" + value + ")"); 94
126 } 95 final Element e = createStaticData(flys, cr, context, name, value, type);
127
128 Element e = createStaticData(flys, cr, context, name, value, type);
129 96
130 ui.appendChild(e); 97 ui.appendChild(e);
131 98
132 } 99 }
133
134 100
135 /** 101 /**
136 * Creates a <i>data</i> element used in the static part of the DESCRIBE 102 * Creates a <i>data</i> element used in the static part of the DESCRIBE
137 * document. 103 * document.
138 * 104 *
139 * @param creator The ElementCreator that is used to build new Elements. 105 * @param creator
140 * @param cc The CallContext object used for nested i18n retrieval. 106 * The ElementCreator that is used to build new Elements.
141 * @param name The name of the data item. 107 * @param cc
142 * @param value The value as string. 108 * The CallContext object used for nested i18n retrieval.
109 * @param name
110 * The name of the data item.
111 * @param value
112 * The value as string.
143 * 113 *
144 * @return an Element. 114 * @return an Element.
145 */ 115 */
146 protected Element createStaticData( 116 protected Element createStaticData(final D4EArtifact flys, final ElementCreator creator, final CallContext cc, final String name, final String value,
147 D4EArtifact flys, 117 final String type) {
148 ElementCreator creator, 118 final Element dataElement = creator.create("data");
149 CallContext cc,
150 String name,
151 String value,
152 String type
153 ) {
154 Element dataElement = creator.create("data");
155 creator.addAttr(dataElement, "name", name, true); 119 creator.addAttr(dataElement, "name", name, true);
156 creator.addAttr(dataElement, "type", type, true); 120 creator.addAttr(dataElement, "type", type, true);
157 121
158 Element itemElement = creator.create("item"); 122 final Element itemElement = creator.create("item");
159 creator.addAttr(itemElement, "value", value, true); 123 creator.addAttr(itemElement, "value", value, true);
160 124
161 creator.addAttr( 125 creator.addAttr(itemElement, "label", getLabelFor(cc, name, value, type), true);
162 itemElement,
163 "label",
164 getLabelFor(cc, name, value, type),
165 true);
166 126
167 dataElement.appendChild(itemElement); 127 dataElement.appendChild(itemElement);
168 128
169 return dataElement; 129 return dataElement;
170 } 130 }
171
172 131
173 /** 132 /**
174 * @param cc 133 * @param cc
175 * @param name 134 * @param name
176 * @param value 135 * @param value
177 * @param type 136 * @param type
178 * 137 *
179 * @return 138 * @return
180 */ 139 */
181 protected String getLabelFor( 140 protected String getLabelFor(final CallContext cc, final String name, final String value, final String type) {
182 CallContext cc, 141 final CallMeta meta = cc.getMeta();
183 String name,
184 String value,
185 String type
186 ) {
187 CallMeta meta = cc.getMeta();
188 142
189 try { 143 try {
190 // XXX A better way to format the output would be to use the 144 // XXX A better way to format the output would be to use the
191 // 'type' value of the data objects. 145 // 'type' value of the data objects.
192 double doubleVal = Double.parseDouble(value); 146 final double doubleVal = Double.parseDouble(value);
193 Locale l = Resources.getLocale(meta); 147 final Locale l = Resources.getLocale(meta);
194 NumberFormat nf = NumberFormat.getInstance(l); 148 final NumberFormat nf = NumberFormat.getInstance(l);
195 149
196 return nf.format(doubleVal); 150 return nf.format(doubleVal);
197 } 151 }
198 catch (NumberFormatException nfe) { 152 catch (final NumberFormatException nfe) {
199 return Resources.getMsg(meta, value, value); 153 return Resources.getMsg(meta, value, value);
200 } 154 }
201 } 155 }
202
203 156
204 /** 157 /**
205 * This method returns the default value and label for <i>data</i>. 158 * This method returns the default value and label for <i>data</i>.
206 * 159 *
207 * Override this method in a subclass to set an appropiate default 160 * Override this method in a subclass to set an appropiate default
208 * value. 161 * value.
209 * The default label can be ignored by the client (e.g. the gwt-client). 162 * The default label can be ignored by the client (e.g. the gwt-client).
210 * but shall not be null. 163 * but shall not be null.
211 * 164 *
212 * Example implementation: 165 * Example implementation:
213 * if (data != null && data.getName().equals("the_answer")) { 166 * if (data != null && data.getName().equals("the_answer")) {
214 * return new String[] {"42", "the_answer"}; 167 * return new String[] {"42", "the_answer"};
215 * } 168 * }
216 * 169 *
217 * @param context The CallContext used for i18n. 170 * @param context
218 * @param data The data objects that the defaults are for. 171 * The CallContext used for i18n.
172 * @param data
173 * The data objects that the defaults are for.
219 * @return a String[] with [default value, default label]. 174 * @return a String[] with [default value, default label].
220 */ 175 */
221 protected String[] getDefaultsFor(CallContext context, StateData data) { 176 protected String[] getDefaultsFor(final CallContext context, final StateData data) {
222 return null; 177 return null;
223 } 178 }
224 179
225 180 @Override
226 public Element describe( 181 public Element describe(final Artifact artifact, final Document document, final Node root, final CallContext context, final String uuid) {
227 Artifact artifact, 182 final ElementCreator creator = new ElementCreator(document, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);
228 Document document,
229 Node root,
230 CallContext context,
231 String uuid)
232 {
233 ElementCreator creator = new ElementCreator(
234 document,
235 ArtifactNamespaceContext.NAMESPACE_URI,
236 ArtifactNamespaceContext.NAMESPACE_PREFIX);
237 183
238 String helpText = getHelpText(); 184 String helpText = getHelpText();
239 if (helpText != null) { 185 if (helpText != null) {
240 helpText = replaceHelpUrl( 186 helpText = replaceHelpUrl(Resources.getMsg(context.getMeta(), helpText, helpText));
241 Resources.getMsg(context.getMeta(), helpText, helpText)); 187 }
242 } 188
243 189 Element ui = null;
244 Element ui = null; 190 final String uiprovider = getUIProvider();
245 String uiprovider = getUIProvider();
246 if (uiprovider != null) { 191 if (uiprovider != null) {
247 ui = ProtocolUtils.createArtNode( 192 ui = ProtocolUtils.createArtNode(creator, "dynamic", new String[] { "uiprovider", "helpText" }, new String[] { uiprovider, helpText });
248 creator, "dynamic", 193 } else {
249 new String[] { "uiprovider", "helpText" }, 194 ui = ProtocolUtils.createArtNode(creator, "dynamic", new String[] { "helpText" }, new String[] { helpText });
250 new String[] { uiprovider, helpText }); 195 }
251 } 196
252 else { 197 final Map<String, StateData> theData = getData();
253 ui = ProtocolUtils.createArtNode(
254 creator, "dynamic",
255 new String[] { "helpText" },
256 new String[] { helpText });
257 }
258
259 Map<String, StateData> theData = getData();
260 if (theData == null) { 198 if (theData == null) {
261 return ui; 199 return ui;
262 } 200 }
263 201
264 D4EArtifact flys = (D4EArtifact)artifact; 202 final D4EArtifact flys = (D4EArtifact) artifact;
265 203
266 for (String name: theData.keySet()) { 204 for (final String name : theData.keySet()) {
267 StateData data = getData(flys, name); 205 StateData data = getData(flys, name);
268 206
269 if (data == null) { 207 if (data == null) {
270 data = getData(name); 208 data = getData(name);
271 } 209 }
272 210
273 Element select = createData(creator, artifact, data, context); 211 final Element select = createData(creator, artifact, data, context);
274 212
275 String[] defaults = getDefaultsFor(context, data); 213 final String[] defaults = getDefaultsFor(context, data);
276 if (defaults != null && defaults.length > 1) { 214 if (defaults != null && defaults.length > 1) {
277 creator.addAttr(select, "defaultValue", defaults[0], true); 215 creator.addAttr(select, "defaultValue", defaults[0], true);
278 creator.addAttr(select, "defaultLabel", defaults[1], true); 216 creator.addAttr(select, "defaultLabel", defaults[1], true);
279 } 217 }
280 218
283 } 221 }
284 222
285 return ui; 223 return ui;
286 } 224 }
287 225
288
289 /** 226 /**
290 * @param artifact 227 * @param artifact
291 * @param creator 228 * @param creator
292 * @param name 229 * @param name
293 * @param context 230 * @param context
294 * @param select 231 * @param select
295 */ 232 */
296 protected void appendItems( 233 protected void appendItems(final Artifact artifact, final ElementCreator creator, final String name, final CallContext context, final Element select) {
297 Artifact artifact, 234 final Element choices = ProtocolUtils.createArtNode(creator, "choices", null, null);
298 ElementCreator creator,
299 String name,
300 CallContext context,
301 Element select
302 ) {
303 Element choices = ProtocolUtils.createArtNode(
304 creator, "choices", null, null);
305 235
306 select.appendChild(choices); 236 select.appendChild(choices);
307 237
308 Element[] items = createItems(creator, artifact, name, context); 238 final Element[] items = createItems(creator, artifact, name, context);
309 if (items != null) { 239 if (items != null) {
310 for (Element item: items) { 240 for (final Element item : items) {
311 choices.appendChild(item); 241 choices.appendChild(item);
312 } 242 }
313 } 243 }
314 } 244 }
315 245
316
317 /** 246 /**
318 * This method creates the root node that contains the list of selectable 247 * This method creates the root node that contains the list of selectable
319 * items. 248 * items.
320 * 249 *
321 * @param cr The ElementCreator. 250 * @param cr
251 * The ElementCreator.
322 * 252 *
323 * @return the root node of the item list. 253 * @return the root node of the item list.
324 */ 254 */
325 protected Element createData( 255 protected Element createData(final ElementCreator cr, final Artifact artifact, final StateData data, final CallContext context) {
326 ElementCreator cr, 256 final Element select = ProtocolUtils.createArtNode(cr, "select", null, null);
327 Artifact artifact,
328 StateData data,
329 CallContext context)
330 {
331 Element select = ProtocolUtils.createArtNode(
332 cr, "select", null, null);
333 cr.addAttr(select, "name", data.getName(), true); 257 cr.addAttr(select, "name", data.getName(), true);
334 258
335 Element label = ProtocolUtils.createArtNode( 259 final Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
336 cr, "label", null, null);
337 260
338 select.appendChild(label); 261 select.appendChild(label);
339 262
340 label.setTextContent(Resources.getMsg( 263 label.setTextContent(Resources.getMsg(context.getMeta(), getID(), getID()));
341 context.getMeta(),
342 getID(),
343 getID()));
344 264
345 return select; 265 return select;
346 } 266 }
347
348 267
349 /** 268 /**
350 * This method creates a list of items. These items represent the amount of 269 * This method creates a list of items. These items represent the amount of
351 * input data that is possible for this state. 270 * input data that is possible for this state.
352 * 271 *
353 * @param cr The ElementCreator. 272 * @param cr
354 * @param name The name of the amount of data. 273 * The ElementCreator.
274 * @param name
275 * The name of the amount of data.
355 * 276 *
356 * @return a list of items. 277 * @return a list of items.
357 */ 278 */
358 protected Element[] createItems( 279 protected Element[] createItems(final ElementCreator cr, final Artifact artifact, final String name, final CallContext context) {
359 ElementCreator cr, 280 return null;
360 Artifact artifact, 281 }
361 String name,
362 CallContext context
363 ) {
364 return null;
365 }
366
367 282
368 /** 283 /**
369 * This method is used to create an <i>item</i> Element that contains two 284 * This method is used to create an <i>item</i> Element that contains two
370 * further elements <i>label</i> and <i>value</i>. The label and value 285 * further elements <i>label</i> and <i>value</i>. The label and value
371 * elements both have text nodes. 286 * elements both have text nodes.
372 * 287 *
373 * @param cr The ElementCreator used to build new Elements. 288 * @param cr
374 * @param obj This implementation awaits a String array with [0] = label and 289 * The ElementCreator used to build new Elements.
375 * [1] = value. 290 * @param obj
291 * This implementation awaits a String array with [0] = label and
292 * [1] = value.
376 * 293 *
377 * @return an Element. 294 * @return an Element.
378 */ 295 */
379 protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { 296 protected static Element createItem(final XMLUtils.ElementCreator cr, final Object obj) {
380 Element item = ProtocolUtils.createArtNode(cr, "item", null, null); 297 final Element item = ProtocolUtils.createArtNode(cr, "item", null, null);
381 Element label = ProtocolUtils.createArtNode(cr, "label", null, null); 298 final Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
382 Element value = ProtocolUtils.createArtNode(cr, "value", null, null); 299 final Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
383 300
384 String[] arr = (String[]) obj; 301 final String[] arr = (String[]) obj;
385 302
386 label.setTextContent(arr[0]); 303 label.setTextContent(arr[0]);
387 value.setTextContent(arr[1]); 304 value.setTextContent(arr[1]);
388 305
389 item.appendChild(label); 306 item.appendChild(label);
390 item.appendChild(value); 307 item.appendChild(value);
391 308
392 return item; 309 return item;
393 } 310 }
394 311
312 // protected static Element createItem(final XMLUtils.ElementCreator cr, final Object obj) {
313 // final Element item = ProtocolUtils.createArtNode(cr, "item", null, null);
314 // final Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
315 // final Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
316 //
317 // final String[] arr = (String[]) obj;
318 //
319 // label.setTextContent(arr[0]);
320 // value.setTextContent(arr[1]);
321 //
322 // item.appendChild(label);
323 // item.appendChild(value);
324 //
325 // return item;
326 // }
395 327
396 /** 328 /**
397 * This method transform a given value into a StateData object. 329 * This method transform a given value into a StateData object.
398 * 330 *
399 * @param flys The D4EArtifact. 331 * @param flys
400 * @param name The name of the data object. 332 * The D4EArtifact.
401 * @param val The value of the data object. 333 * @param name
334 * The name of the data object.
335 * @param val
336 * The value of the data object.
402 * 337 *
403 * @return a StateData object with <i>name</i> and <i>val</i>ue. 338 * @return a StateData object with <i>name</i> and <i>val</i>ue.
404 */ 339 */
405 public StateData transform( 340 public StateData transform(final D4EArtifact flys, final CallContext cc, final StateData stateData, final String name, final String val) {
406 D4EArtifact flys,
407 CallContext cc,
408 StateData stateData,
409 String name,
410 String val
411 ) {
412 if (log.isDebugEnabled()) { 341 if (log.isDebugEnabled()) {
413 log.debug("Transform data ('" + name + "','" + val + "')"); 342 log.debug("Transform data ('" + name + "','" + val + "')");
414 } 343 }
415 344
416 stateData.setValue(val); 345 stateData.setValue(val);
417 346
418 return stateData; 347 return stateData;
419 } 348 }
420 349
421 350 /**
422 /** Override this to do validation. 351 * Override this to do validation.
423 * 352 *
424 * Throw an IllegalArgumentException with a localized 353 * Throw an IllegalArgumentException with a localized
425 * error message that should be presented to the user in case 354 * error message that should be presented to the user in case
426 * the date provided is invalid. */ 355 * the date provided is invalid.
427 public void validate(Artifact artifact, CallContext context) 356 */
428 throws IllegalArgumentException { 357 public void validate(final Artifact artifact, final CallContext context) throws IllegalArgumentException {
429 validate(artifact); /* For compatibility so that classes that 358 validate(artifact); /*
430 override this method still work. */ 359 * For compatibility so that classes that
431 } 360 * override this method still work.
361 */
362 }
363
432 /** 364 /**
433 * This method is deprecated. 365 * This method is deprecated.
434 * Override the function with the callcontext instead to do 366 * Override the function with the callcontext instead to do
435 * localization of error.s 367 * localization of error.s
436 */ 368 */
437 public boolean validate(Artifact artifact) 369 public boolean validate(final Artifact artifact) throws IllegalArgumentException {
438 throws IllegalArgumentException
439 {
440 return true; 370 return true;
441 } 371 }
442 372
443
444 /** 373 /**
445 * Returns which UIProvider shall be used to aid user input. 374 * Returns which UIProvider shall be used to aid user input.
446 */ 375 */
447 protected String getUIProvider() { 376 protected String getUIProvider() {
448 return null; 377 return null;
449 } 378 }
450 379
451 380 public Object computeAdvance(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) {
452 public Object computeAdvance( 381 return null;
453 D4EArtifact artifact, 382 }
454 String hash, 383
455 CallContext context, 384 public Object computeFeed(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) {
456 List<Facet> facets, 385 return null;
457 Object old 386 }
458 ) { 387
459 return null; 388 public Object computeInit(final D4EArtifact artifact, final String hash, final Object context, final CallMeta meta, final List<Facet> facets) {
460 }
461
462
463 public Object computeFeed(
464 D4EArtifact artifact,
465 String hash,
466 CallContext context,
467 List<Facet> facets,
468 Object old
469 ) {
470 return null;
471 }
472
473
474 public Object computeInit(
475 D4EArtifact artifact,
476 String hash,
477 Object context,
478 CallMeta meta,
479 List<Facet> facets)
480 {
481 return null; 389 return null;
482 } 390 }
483 } 391 }
484 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : 392 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org