001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/graphics/sld/SLDFactory.java $
002 /*----------------------------------------------------------------------------
003 This file is part of deegree, http://deegree.org/
004 Copyright (C) 2001-2009 by:
005 Department of Geography, University of Bonn
006 and
007 lat/lon GmbH
008
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036 package org.deegree.graphics.sld;
037
038 import static java.lang.Float.parseFloat;
039 import static java.lang.StrictMath.sqrt;
040 import static org.deegree.framework.xml.XMLTools.getElement;
041 import static org.deegree.framework.xml.XMLTools.getNodeAsDouble;
042 import static org.deegree.ogcbase.CommonNamespaces.SLDNS;
043 import static org.deegree.ogcbase.CommonNamespaces.SLD_PREFIX;
044
045 import java.io.IOException;
046 import java.io.StringReader;
047 import java.net.MalformedURLException;
048 import java.net.URI;
049 import java.net.URL;
050 import java.util.ArrayList;
051 import java.util.HashMap;
052 import java.util.Iterator;
053 import java.util.LinkedList;
054 import java.util.List;
055
056 import org.deegree.datatypes.QualifiedName;
057 import org.deegree.framework.log.ILogger;
058 import org.deegree.framework.log.LoggerFactory;
059 import org.deegree.framework.xml.ElementList;
060 import org.deegree.framework.xml.NamespaceContext;
061 import org.deegree.framework.xml.XMLFragment;
062 import org.deegree.framework.xml.XMLParsingException;
063 import org.deegree.framework.xml.XMLTools;
064 import org.deegree.model.filterencoding.AbstractFilter;
065 import org.deegree.model.filterencoding.ComplexFilter;
066 import org.deegree.model.filterencoding.Expression;
067 import org.deegree.model.filterencoding.FalseFilter;
068 import org.deegree.model.filterencoding.Filter;
069 import org.deegree.model.filterencoding.FilterEvaluationException;
070 import org.deegree.model.filterencoding.LogicalOperation;
071 import org.deegree.model.filterencoding.Operation;
072 import org.deegree.model.filterencoding.OperationDefines;
073 import org.deegree.ogcbase.CommonNamespaces;
074 import org.deegree.ogcbase.OGCDocument;
075 import org.deegree.ogcbase.PropertyPath;
076 import org.w3c.dom.Element;
077 import org.w3c.dom.Node;
078 import org.w3c.dom.NodeList;
079 import org.w3c.dom.Text;
080 import org.xml.sax.SAXException;
081
082 /**
083 * Factory class for all mapped SLD-elements.
084 * <p>
085 * TODO: Default values for omitted elements (such as fill color) should better not be used in the construction of the
086 * corresponding objects (Fill), but marked as left out (to make it possible to differentiate between explicitly given
087 * values and default values).
088 * <p>
089 *
090 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
091 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
092 * @version $Revision: 18195 $ $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
093 */
094 public class SLDFactory {
095
096 private static final ILogger LOG = LoggerFactory.getLogger( SLDFactory.class );
097
098 private static URI ogcNS = CommonNamespaces.OGCNS;
099
100 private static URI xlnNS = CommonNamespaces.XLNNS;
101
102 private static final String PSE = CommonNamespaces.SE_PREFIX + ":";
103
104 private static NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
105
106 private static XMLFragment sldDoc = null;
107
108 /**
109 * Creates a <tt>StyledLayerDescriptor</tt>-instance from the given XML-representation.
110 * <p>
111 *
112 * @param s
113 * contains the XML document
114 * @throws XMLParsingException
115 * if a syntactic or semantic error in the XML document is encountered
116 * @return the constructed <tt>StyledLayerDescriptor</tt>-instance
117 */
118 public static synchronized StyledLayerDescriptor createSLD( String s )
119 throws XMLParsingException {
120 StyledLayerDescriptor sld = null;
121 try {
122 sldDoc = new XMLFragment();
123 sldDoc.load( new StringReader( s.trim() ), XMLFragment.DEFAULT_URL );
124 sld = createSLD( sldDoc );
125 } catch ( IOException e ) {
126 LOG.logDebug( e.getMessage(), e );
127 throw new XMLParsingException( "IOException encountered while parsing SLD-Document" );
128 } catch ( SAXException e ) {
129 LOG.logDebug( e.getMessage(), e );
130 throw new XMLParsingException( "SAXException encountered while parsing SLD-Document" );
131 }
132
133 return sld;
134 }
135
136 /**
137 * Creates a <tt>StyledLayerDescriptor</tt>-instance from a SLD document read from the passed URL
138 *
139 * @param url
140 * @return the SLD bean
141 * @throws XMLParsingException
142 */
143 public static synchronized StyledLayerDescriptor createSLD( URL url )
144 throws XMLParsingException {
145 StyledLayerDescriptor sld = null;
146
147 try {
148 sldDoc = new XMLFragment();
149 sldDoc.load( url );
150 sld = createSLD( sldDoc );
151 } catch ( IOException e ) {
152 LOG.logError( e.getMessage(), e );
153 throw new XMLParsingException( "IOException encountered while parsing SLD-Document" );
154 } catch ( SAXException e ) {
155 LOG.logError( e.getMessage(), e );
156 throw new XMLParsingException( "SAXException encountered while parsing SLD-Document" );
157 }
158
159 return sld;
160 }
161
162 /**
163 * Creates a <tt>StyledLayerDescriptor</tt>-instance according to the contents of the DOM-subtree starting at the
164 * given 'StyledLayerDescriptor'-<tt>Element</tt>.
165 * <p>
166 *
167 * @param sldDoc
168 *
169 * @throws XMLParsingException
170 * if a syntactic or semantic error in the DOM-subtree is encountered
171 * @return the constructed <tt>StyledLayerDescriptor</tt>-instance
172 */
173 public static StyledLayerDescriptor createSLD( XMLFragment sldDoc )
174 throws XMLParsingException {
175
176 Element element = sldDoc.getRootElement();
177
178 // optional: <Name>
179 String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
180
181 // optional: <Title>
182 String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
183 // optional: <Abstract>
184 String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
185 // required: version-Attribute
186 String version = XMLTools.getRequiredAttrValue( "version", null, element );
187
188 // optional: <NamedLayer>(s) / <UserLayer>(s)
189 NodeList nodelist = element.getChildNodes();
190 ArrayList<AbstractLayer> layerList = new ArrayList<AbstractLayer>( 100 );
191
192 for ( int i = 0; i < nodelist.getLength(); i++ ) {
193 if ( nodelist.item( i ) instanceof Element ) {
194 Element child = (Element) nodelist.item( i );
195 String namespace = child.getNamespaceURI();
196 if ( !CommonNamespaces.SLDNS.toASCIIString().equals( namespace ) ) {
197 continue;
198 }
199
200 String childName = child.getLocalName();
201 if ( childName.equals( "NamedLayer" ) ) {
202 layerList.add( createNamedLayer( child ) );
203 } else if ( childName.equals( "UserLayer" ) ) {
204 layerList.add( createUserLayer( child ) );
205 }
206 }
207 }
208
209 AbstractLayer[] al = new AbstractLayer[layerList.size()];
210 AbstractLayer[] layers = layerList.toArray( al );
211
212 return new StyledLayerDescriptor( name, title, version, abstract_, layers );
213 }
214
215 private static Categorize createCategorize( Element root )
216 throws XMLParsingException {
217 // ignore fallback value, we really implement it
218 // String fallbackValue = root.getAttribute( "fallbackValue" );
219
220 Categorize categorize = new Categorize();
221
222 // ignore lookup value element, should be set to "Rasterdata"
223 // Element lv = XMLTools.getElement( root, PSE + "LookupValue", nsContext );
224 // ParameterValueType lookupValue = lv == null ? null : createParameterValueType( lv );
225 //
226 // if ( lookupValue != null ) {
227 // categorize.setLookupValue( lookupValue );
228 // }
229
230 List<Element> valueElements = XMLTools.getElements( root, PSE + "Value", nsContext );
231 List<Element> thresholdElements = XMLTools.getElements( root, PSE + "Threshold", nsContext );
232
233 LinkedList<ParameterValueType> values = new LinkedList<ParameterValueType>();
234 LinkedList<ParameterValueType> thresholds = new LinkedList<ParameterValueType>();
235
236 for ( Element e : valueElements ) {
237 values.add( createParameterValueType( e ) );
238 }
239
240 for ( Element e : thresholdElements ) {
241 thresholds.add( createParameterValueType( e ) );
242 }
243
244 categorize.setValues( values );
245 categorize.setThresholds( thresholds );
246
247 String tbt = root.getAttribute( "threshholdsBelongTo" );
248 if ( tbt == null ) {
249 tbt = root.getAttribute( "thresholdsBelongTo" );
250 }
251
252 ThresholdsBelongTo thresholdsBelongTo = null;
253
254 if ( tbt != null ) {
255 if ( tbt.equalsIgnoreCase( "succeeding" ) ) {
256 thresholdsBelongTo = ThresholdsBelongTo.SUCCEEDING;
257 }
258 if ( tbt.equalsIgnoreCase( "preceding" ) ) {
259 thresholdsBelongTo = ThresholdsBelongTo.PRECEDING;
260 }
261 }
262
263 if ( thresholdsBelongTo != null ) {
264 categorize.setThresholdsBelongTo( thresholdsBelongTo );
265 }
266
267 return categorize;
268 }
269
270 /**
271 *
272 * @param root
273 * @param min
274 * @param max
275 * @return a raster symbolizer
276 * @throws XMLParsingException
277 */
278 private static RasterSymbolizer createRasterSymbolizer( Element root, double min, double max )
279 throws XMLParsingException {
280 RasterSymbolizer symbolizer = new RasterSymbolizer( min, max );
281
282 if ( root.getAttribute( "shaded" ) != null && root.getAttribute( "shaded" ).equals( "true" ) ) {
283 symbolizer.setShaded( true );
284
285 String kernelAtt = root.getAttribute( "kernel" );
286 if ( kernelAtt == null || kernelAtt.trim().length() == 0 ) {
287 kernelAtt = "0, 1, 2, -1, 1, 1, -2, -1, 0";
288 }
289 String[] vals = kernelAtt.split( "[,]" );
290 float[] kernel = new float[vals.length];
291 for ( int i = 0; i < vals.length; ++i ) {
292 kernel[i] = parseFloat( vals[i] );
293 }
294
295 symbolizer.setShadeKernel( (int) sqrt( kernel.length ), kernel );
296 }
297
298 Element opacity = XMLTools.getElement( root, PSE + "Opacity", nsContext );
299 if ( opacity != null ) {
300 symbolizer.setOpacity( createParameterValueType( opacity ) );
301 }
302
303 Element colorMap = XMLTools.getElement( root, PSE + "ColorMap", nsContext );
304 if ( colorMap != null ) {
305 Element categorize = XMLTools.getElement( colorMap, PSE + "Categorize", nsContext );
306
307 if ( categorize != null ) {
308 symbolizer.setCategorize( createCategorize( categorize ) );
309 }
310
311 Element interpolate = XMLTools.getElement( colorMap, PSE + "Interpolate", nsContext );
312
313 if ( interpolate != null ) {
314 symbolizer.setInterpolate( createInterpolate( interpolate ) );
315 }
316 }
317
318 Element contrastEnhancement = getElement( root, PSE + "ContrastEnhancement", nsContext );
319 if ( contrastEnhancement != null ) {
320 symbolizer.setGamma( getNodeAsDouble( contrastEnhancement, PSE + "GammaValue", nsContext, 1d ) );
321 }
322
323 return symbolizer;
324 }
325
326 /**
327 * @param root
328 * @return an Interpolate object
329 * @throws XMLParsingException
330 */
331 private static Interpolate createInterpolate( Element root )
332 throws XMLParsingException {
333 String fallbackValue = root.getAttribute( "fallbackValue" );
334
335 Interpolate interpolate = new Interpolate( fallbackValue );
336
337 Element elem = XMLTools.getElement( root, PSE + "lookupValue", nsContext );
338 if ( elem != null ) {
339 interpolate.setLookupValue( createParameterValueType( elem ) );
340 }
341
342 String mode = root.getAttribute( "mode" );
343 if ( mode != null ) {
344 if ( mode.equalsIgnoreCase( "linear" ) ) {
345 interpolate.setMode( Mode.LINEAR );
346 }
347 if ( mode.equalsIgnoreCase( "cosine" ) ) {
348 LOG.logWarning( "Cosine interpolation is not supported." );
349 interpolate.setMode( Mode.COSINE );
350 }
351 if ( mode.equalsIgnoreCase( "cubic" ) ) {
352 LOG.logWarning( "Cubic interpolation is not supported." );
353 interpolate.setMode( Mode.CUBIC );
354 }
355 }
356
357 String method = root.getAttribute( "method" );
358 if ( method != null ) {
359 if ( method.equalsIgnoreCase( "numeric" ) ) {
360 LOG.logWarning( "Numeric method is not supported, using color method anyway." );
361 interpolate.setMethod( Method.NUMERIC );
362 }
363 if ( method.equalsIgnoreCase( "color" ) ) {
364 interpolate.setMethod( Method.COLOR );
365 }
366 }
367
368 List<Element> ips = XMLTools.getElements( root, PSE + "InterpolationPoint", nsContext );
369
370 interpolate.setInterpolationPoints( createInterpolationPoints( ips ) );
371
372 return interpolate;
373 }
374
375 private static List<InterpolationPoint> createInterpolationPoints( List<Element> ips )
376 throws XMLParsingException {
377 List<InterpolationPoint> ps = new ArrayList<InterpolationPoint>( ips.size() );
378
379 for ( Element elem : ips ) {
380 double data = XMLTools.getRequiredNodeAsDouble( elem, PSE + "Data", nsContext );
381 Element e = XMLTools.getRequiredElement( elem, PSE + "Value", nsContext );
382 try {
383 String val = createParameterValueType( e ).evaluate( null ).substring( 1 );
384 ps.add( new InterpolationPoint( data, val ) );
385 } catch ( NumberFormatException e1 ) {
386 LOG.logError( "A 'Value' in an 'InterpolationPoint' could not be parsed.", e1 );
387 } catch ( FilterEvaluationException e1 ) {
388 LOG.logError( "A 'Value' in an 'InterpolationPoint' could not be parsed.", e1 );
389 }
390 }
391
392 return ps;
393 }
394
395 /**
396 * Creates a <tt>TextSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
397 * 'TextSymbolizer'-<tt>Element</tt>.
398 * <p>
399 *
400 * @param element
401 * the 'TextSymbolizer'-<tt>Element</tt>
402 * @param min
403 * scale-constraint to be used
404 * @param max
405 * scale-constraint to be used
406 * @throws XMLParsingException
407 * if a syntactic or semantic error in the DOM-subtree is encountered
408 * @return the constructed <tt>TextSymbolizer</tt>-instance
409 */
410 private static TextSymbolizer createTextSymbolizer( Element element, double min, double max )
411 throws XMLParsingException {
412
413 // optional: <Geometry>
414 Geometry geometry = null;
415 Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
416
417 if ( geometryElement != null ) {
418 geometry = createGeometry( geometryElement );
419 }
420
421 // optional: <Label>
422 ParameterValueType label = null;
423 Element labelElement = XMLTools.getChildElement( "Label", CommonNamespaces.SLDNS, element );
424
425 if ( labelElement != null ) {
426 label = createParameterValueType( labelElement );
427 }
428
429 // optional: <Font>
430 Font font = null;
431 Element fontElement = XMLTools.getChildElement( "Font", CommonNamespaces.SLDNS, element );
432
433 if ( fontElement != null ) {
434 font = createFont( fontElement );
435 }
436
437 // optional: <LabelPlacement>
438 LabelPlacement labelPlacement = null;
439 Element lpElement = XMLTools.getChildElement( "LabelPlacement", CommonNamespaces.SLDNS, element );
440
441 if ( lpElement != null ) {
442 labelPlacement = createLabelPlacement( lpElement );
443 } else {
444 PointPlacement pp = StyleFactory.createPointPlacement();
445 labelPlacement = StyleFactory.createLabelPlacement( pp );
446 }
447
448 // optional: <Halo>
449 Halo halo = null;
450 Element haloElement = XMLTools.getChildElement( "Halo", SLDNS, element );
451
452 if ( haloElement != null ) {
453 halo = createHalo( haloElement );
454 }
455
456 // optional: <Fill>
457 Fill fill = null;
458
459 TextSymbolizer ps = null;
460
461 // deegree specific extension:
462 Element bbox = getElement( element, SLD_PREFIX + ":BoundingBox", nsContext );
463 if ( bbox != null ) {
464 try {
465 ParameterValueType minx = createParameterValueType( getElement( bbox, SLD_PREFIX + ":Minx", nsContext ) );
466 ParameterValueType miny = createParameterValueType( getElement( bbox, SLD_PREFIX + ":Miny", nsContext ) );
467 ParameterValueType maxx = createParameterValueType( getElement( bbox, SLD_PREFIX + ":Maxx", nsContext ) );
468 ParameterValueType maxy = createParameterValueType( getElement( bbox, SLD_PREFIX + ":Maxy", nsContext ) );
469
470 ps = new TextSymbolizer( geometry, label, font, halo, minx, miny, maxx, maxy );
471 } catch ( NullPointerException npe ) {
472 npe.printStackTrace();
473 LOG.logError( "One of Minx, Miny, Maxx, Maxy was missing in a BoundingBox." );
474 }
475 } else {
476
477 String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
478 if ( respClass == null ) {
479 ps = new TextSymbolizer( geometry, label, font, labelPlacement, halo, fill, min, max );
480 } else {
481 ps = new TextSymbolizer( geometry, respClass, label, font, labelPlacement, halo, fill, min, max );
482 }
483 }
484
485 return ps;
486 }
487
488 /**
489 * Creates a <tt>Halo</tt>-instance according to the contents of the DOM-subtree starting at the given 'Halo'-
490 * <tt>Element</tt>.
491 * <p>
492 *
493 * @param element
494 * the 'Halo'-<tt>Element</tt>
495 * @throws XMLParsingException
496 * if a syntactic or semantic error in the DOM-subtree is encountered
497 * @return the constructed <tt>Halo</tt>-instance
498 */
499 private static Halo createHalo( Element element )
500 throws XMLParsingException {
501 // optional: <Radius>
502 ParameterValueType radius = null;
503 Element radiusElement = XMLTools.getChildElement( "Radius", CommonNamespaces.SLDNS, element );
504
505 if ( radiusElement != null ) {
506 radius = createParameterValueType( radiusElement );
507 }
508
509 // optional: <Fill>
510 Fill fill = null;
511 Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
512
513 if ( fillElement != null ) {
514 fill = createFill( fillElement );
515 }
516
517 // optional: <Stroke>
518 Stroke stroke = null;
519 Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
520
521 if ( strokeElement != null ) {
522 stroke = createStroke( strokeElement );
523 }
524
525 return new Halo( radius, fill, stroke );
526 }
527
528 /**
529 * Creates a <tt>LabelPlacement</tt>-instance according to the contents of the DOM-subtree starting at the given
530 * 'LabelPlacement'-<tt>Element</tt>.
531 * <p>
532 *
533 * @param element
534 * the 'LabelPlacement'-<tt>Element</tt>
535 * @throws XMLParsingException
536 * if a syntactic or semantic error in the DOM-subtree is encountered
537 * @return the constructed <tt>LabelPlacement</tt>-instance
538 */
539 private static LabelPlacement createLabelPlacement( Element element )
540 throws XMLParsingException {
541 LabelPlacement labelPlacement = null;
542
543 // required: <PointPlacement> / <LinePlacement>
544 NodeList nodelist = element.getChildNodes();
545 PointPlacement pPlacement = null;
546 LinePlacement lPlacement = null;
547
548 for ( int i = 0; i < nodelist.getLength(); i++ ) {
549 if ( nodelist.item( i ) instanceof Element ) {
550 Element child = (Element) nodelist.item( i );
551 String namespace = child.getNamespaceURI();
552
553 if ( !CommonNamespaces.SLDNS.toASCIIString().equals( namespace ) ) {
554 continue;
555 }
556
557 String childName = child.getLocalName();
558
559 if ( childName.equals( "PointPlacement" ) ) {
560 pPlacement = createPointPlacement( child );
561 } else if ( childName.equals( "LinePlacement" ) ) {
562 lPlacement = createLinePlacement( child );
563 }
564 }
565 }
566
567 if ( ( pPlacement != null ) && ( lPlacement == null ) ) {
568 labelPlacement = new LabelPlacement( pPlacement );
569 } else if ( ( pPlacement == null ) && ( lPlacement != null ) ) {
570 labelPlacement = new LabelPlacement( lPlacement );
571 } else {
572 throw new XMLParsingException( "Element 'LabelPlacement' must contain exactly one "
573 + "'PointPlacement'- or one 'LinePlacement'-element!" );
574 }
575
576 return labelPlacement;
577 }
578
579 /**
580 * Creates a <tt>PointPlacement</tt>-instance according to the contents of the DOM-subtree starting at the given
581 * 'PointPlacement'-<tt>Element</tt>.
582 * <p>
583 *
584 * @param element
585 * the 'PointPlacement'-<tt>Element</tt>
586 * @throws XMLParsingException
587 * if a syntactic or semantic error in the DOM-subtree is encountered
588 * @return the constructed <tt>PointPlacement</tt>-instance
589 */
590 private static PointPlacement createPointPlacement( Element element )
591 throws XMLParsingException {
592
593 // optional: auto-Attribute (this is deegree-specific)
594 boolean auto = false;
595 String autoStr = XMLTools.getAttrValue( element, null, "auto", null );
596
597 if ( autoStr != null && autoStr.equals( "true" ) ) {
598 auto = true;
599 }
600
601 // optional: <AnchorPoint>
602 ParameterValueType[] anchorPoint = null;
603 Element apElement = XMLTools.getChildElement( "AnchorPoint", CommonNamespaces.SLDNS, element );
604
605 if ( apElement != null ) {
606 anchorPoint = new ParameterValueType[2];
607
608 Element apXElement = XMLTools.getChildElement( "AnchorPointX", CommonNamespaces.SLDNS, apElement );
609 Element apYElement = XMLTools.getChildElement( "AnchorPointY", CommonNamespaces.SLDNS, apElement );
610
611 if ( ( apXElement == null ) || ( apYElement == null ) ) {
612 throw new XMLParsingException( "Element 'AnchorPoint' must contain exactly one "
613 + "'AnchorPointX'- and one 'AnchorPointY'-element!" );
614 }
615
616 anchorPoint[0] = createParameterValueType( apXElement );
617 anchorPoint[1] = createParameterValueType( apYElement );
618 }
619
620 // optional: <Displacement>
621 ParameterValueType[] displacement = null;
622 Element dElement = XMLTools.getChildElement( "Displacement", CommonNamespaces.SLDNS, element );
623
624 if ( dElement != null ) {
625 displacement = new ParameterValueType[2];
626
627 Element dXElement = XMLTools.getChildElement( "DisplacementX", CommonNamespaces.SLDNS, dElement );
628 Element dYElement = XMLTools.getChildElement( "DisplacementY", CommonNamespaces.SLDNS, dElement );
629
630 if ( ( dXElement == null ) || ( dYElement == null ) ) {
631 throw new XMLParsingException( "Element 'Displacement' must contain exactly one "
632 + "'DisplacementX'- and one 'DisplacementY'-element!" );
633 }
634
635 displacement[0] = createParameterValueType( dXElement );
636 displacement[1] = createParameterValueType( dYElement );
637 }
638
639 // optional: <Rotation>
640 ParameterValueType rotation = null;
641 Element rElement = XMLTools.getChildElement( "Rotation", CommonNamespaces.SLDNS, element );
642
643 if ( rElement != null ) {
644 rotation = createParameterValueType( rElement );
645 }
646
647 return new PointPlacement( anchorPoint, displacement, rotation, auto );
648 }
649
650 /**
651 * Creates a <tt>LinePlacement</tt>-instance according to the contents of the DOM-subtree starting at the given
652 * 'LinePlacement'-<tt>Element</tt>.
653 * <p>
654 *
655 * @param element
656 * the 'LinePlacement'-<tt>Element</tt>
657 * @throws XMLParsingException
658 * if a syntactic or semantic error in the DOM-subtree is encountered
659 * @return the constructed <tt>LinePlacement</tt>-instance
660 */
661 private static LinePlacement createLinePlacement( Element element )
662 throws XMLParsingException {
663
664 // optional: <PerpendicularOffset>
665 ParameterValueType pOffset = null;
666 Element pOffsetElement = XMLTools.getChildElement( "PerpendicularOffset", CommonNamespaces.SLDNS, element );
667
668 if ( pOffsetElement != null ) {
669 pOffset = createParameterValueType( pOffsetElement );
670 }
671
672 // optional: <Gap> (this is deegree-specific)
673 ParameterValueType gap = null;
674 Element gapElement = XMLTools.getChildElement( "Gap", CommonNamespaces.SLDNS, element );
675
676 if ( gapElement != null ) {
677 gap = createParameterValueType( gapElement );
678 }
679
680 // optional: <LineWidth> (this is deegree-specific)
681 ParameterValueType lineWidth = null;
682 Element lineWidthElement = XMLTools.getChildElement( "LineWidth", CommonNamespaces.SLDNS, element );
683
684 if ( lineWidthElement != null ) {
685 lineWidth = createParameterValueType( lineWidthElement );
686 }
687
688 return new LinePlacement( pOffset, lineWidth, gap );
689 }
690
691 /**
692 * Creates a <tt>Font</tt>-instance according to the contents of the DOM-subtree starting at the given 'Font'-
693 * <tt>Element</tt>.
694 * <p>
695 *
696 * @param element
697 * the 'Font'-<tt>Element</tt>
698 * @throws XMLParsingException
699 * if a syntactic or semantic error in the DOM-subtree is encountered
700 * @return the constructed <tt>Font</tt>-instance
701 */
702 private static Font createFont( Element element )
703 throws XMLParsingException {
704
705 // optional: <CssParameter>s
706 ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
707 HashMap<String, CssParameter> cssParams = new HashMap<String, CssParameter>( nl.getLength() );
708
709 for ( int i = 0; i < nl.getLength(); i++ ) {
710 CssParameter cssParam = createCssParameter( nl.item( i ) );
711 cssParams.put( cssParam.getName(), cssParam );
712 }
713
714 return new Font( cssParams );
715 }
716
717 /**
718 * Creates a <tt>ParameterValueType</tt>-instance according to the contents of the DOM-subtree starting at the given
719 * <tt>Element</tt>.
720 * <p>
721 *
722 * @param element
723 * the <tt>Element</tt> (must be of the type sld:ParameterValueType)
724 * @throws XMLParsingException
725 * if a syntactic or semantic error in the DOM-subtree is encountered
726 * @return the constructed <tt>ParameterValueType</tt>-instance
727 */
728 private static ParameterValueType createParameterValueType( Element element )
729 throws XMLParsingException {
730 // mix of text nodes and <wfs:Expression>-elements
731 ArrayList<Object> componentList = new ArrayList<Object>();
732 NodeList nl = element.getChildNodes();
733
734 for ( int i = 0; i < nl.getLength(); i++ ) {
735 Node node = nl.item( i );
736
737 switch ( node.getNodeType() ) {
738 case Node.TEXT_NODE: {
739 componentList.add( node.getNodeValue() );
740 break;
741 }
742 case Node.ELEMENT_NODE: {
743 Expression expression = Expression.buildFromDOM( (Element) node );
744 componentList.add( expression );
745 break;
746 }
747 default:
748 throw new XMLParsingException( "Elements of type 'ParameterValueType' may only "
749 + "consist of CDATA and 'ogc:Expression'-elements!" );
750 }
751 }
752
753 Object[] components = componentList.toArray( new Object[componentList.size()] );
754 return new ParameterValueType( components );
755 }
756
757 /**
758 * Creates a <tt>NamedStyle</tt>-instance according to the contents of the DOM-subtree starting at the given
759 * 'NamedStyle'-<tt>Element</tt>.
760 * <p>
761 *
762 * @param element
763 * the 'NamedStyle'-<tt>Element</tt>
764 * @throws XMLParsingException
765 * if a syntactic or semantic error in the DOM-subtree is encountered
766 * @return the constructed <tt>NamedStyle</tt>-instance
767 */
768 private static NamedStyle createNamedStyle( Element element )
769 throws XMLParsingException {
770 // required: <Name>
771 String name = XMLTools.getRequiredStringValue( "Name", CommonNamespaces.SLDNS, element );
772
773 return new NamedStyle( name );
774 }
775
776 /**
777 *
778 */
779 public static NamedStyle createNamedStyle( String name ) {
780 return new NamedStyle( name );
781 }
782
783 /**
784 * Creates a <tt>RemoteOWS</tt>-instance according to the contents of the DOM-subtree starting at the given
785 * 'RemoteOWS'-<tt>Element</tt>.
786 * <p>
787 *
788 * @param element
789 * the 'RemoteOWS'-<tt>Element</tt>
790 * @throws XMLParsingException
791 * if a syntactic or semantic error in the DOM-subtree is encountered
792 * @return the constructed <tt>RemoteOWS</tt>-instance
793 */
794 private static RemoteOWS createRemoteOWS( Element element )
795 throws XMLParsingException {
796 // required: <Service>
797 String service = XMLTools.getRequiredStringValue( "Service", CommonNamespaces.SLDNS, element );
798
799 if ( !( service.equals( "WFS" ) || service.equals( "WCS" ) ) ) {
800 throw new XMLParsingException( "Value ('" + service + "') of element 'service' is invalid. "
801 + "Allowed values are: 'WFS' and 'WCS'." );
802 }
803
804 // required: <OnlineResource>
805 Element onlineResourceElement = XMLTools.getRequiredChildElement( "OnlineResource", CommonNamespaces.SLDNS,
806 element );
807 String href = XMLTools.getRequiredAttrValue( "xlink:href", null, onlineResourceElement );
808 URL url = null;
809
810 try {
811 url = new URL( href );
812 } catch ( MalformedURLException e ) {
813 LOG.logDebug( e.getMessage(), e );
814 throw new XMLParsingException( "Value ('" + href + "') of attribute 'href' of "
815 + "element 'OnlineResoure' does not denote a valid URL" );
816 }
817
818 return new RemoteOWS( service, url );
819 }
820
821 /**
822 * Creates a <tt>NamedLayer</tt>-instance according to the contents of the DOM-subtree starting at the given
823 * 'UserLayer'-<tt>Element</tt>.
824 * <p>
825 *
826 * @param element
827 * the 'NamedLayer'-<tt>Element</tt>
828 * @throws XMLParsingException
829 * if a syntactic or semantic error in the DOM-subtree is encountered
830 * @return the constructed <tt>NamedLayer</tt>-instance
831 */
832 private static NamedLayer createNamedLayer( Element element )
833 throws XMLParsingException {
834 // required: <Name>
835 String name = XMLTools.getRequiredStringValue( "Name", CommonNamespaces.SLDNS, element );
836
837 // optional: <LayerFeatureConstraints>
838 LayerFeatureConstraints lfc = null;
839 Element lfcElement = XMLTools.getChildElement( "LayerFeatureConstraints", CommonNamespaces.SLDNS, element );
840
841 if ( lfcElement != null ) {
842 lfc = createLayerFeatureConstraints( lfcElement );
843 }
844
845 // optional: <NamedStyle>(s) / <UserStyle>(s)
846 NodeList nodelist = element.getChildNodes();
847 ArrayList<AbstractStyle> styleList = new ArrayList<AbstractStyle>();
848
849 for ( int i = 0; i < nodelist.getLength(); i++ ) {
850 if ( nodelist.item( i ) instanceof Element ) {
851 Element child = (Element) nodelist.item( i );
852 String namespace = child.getNamespaceURI();
853
854 if ( !CommonNamespaces.SLDNS.toASCIIString().equals( namespace ) ) {
855 continue;
856 }
857
858 String childName = child.getLocalName();
859
860 if ( childName.equals( "NamedStyle" ) ) {
861 styleList.add( createNamedStyle( child ) );
862 } else if ( childName.equals( "UserStyle" ) ) {
863 styleList.add( createUserStyle( child ) );
864 }
865 }
866 }
867
868 AbstractStyle[] styles = styleList.toArray( new AbstractStyle[styleList.size()] );
869
870 return new NamedLayer( name, lfc, styles );
871 }
872
873 /**
874 *
875 */
876 public static NamedLayer createNamedLayer( String name, LayerFeatureConstraints layerFeatureConstraints,
877 AbstractStyle[] styles ) {
878 return new NamedLayer( name, layerFeatureConstraints, styles );
879 }
880
881 /**
882 * Creates a <tt>UserLayer</tt>-instance according to the contents of the DOM-subtree starting at the given
883 * 'UserLayer'-<tt>Element</tt>.
884 * <p>
885 *
886 * @param element
887 * the 'UserLayer'-<tt>Element</tt>
888 * @throws XMLParsingException
889 * if a syntactic or semantic error in the DOM-subtree is encountered
890 * @return the constructed <tt>UserLayer</tt>-instance
891 */
892 private static UserLayer createUserLayer( Element element )
893 throws XMLParsingException {
894 // optional: <Name>
895 String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
896
897 // optional: <RemoteOWS>
898 RemoteOWS remoteOWS = null;
899 Element remoteOWSElement = XMLTools.getChildElement( "RemoteOWS", CommonNamespaces.SLDNS, element );
900
901 if ( remoteOWSElement != null ) {
902 remoteOWS = createRemoteOWS( remoteOWSElement );
903 }
904
905 // required: <LayerFeatureConstraints>
906 LayerFeatureConstraints lfc = null;
907 Element lfcElement = XMLTools.getRequiredChildElement( "LayerFeatureConstraints", CommonNamespaces.SLDNS,
908 element );
909 lfc = createLayerFeatureConstraints( lfcElement );
910
911 // optional: <UserStyle>(s)
912 ElementList nodelist = XMLTools.getChildElements( "UserStyle", CommonNamespaces.SLDNS, element );
913 UserStyle[] styles = new UserStyle[nodelist.getLength()];
914 for ( int i = 0; i < nodelist.getLength(); i++ ) {
915 styles[i] = createUserStyle( nodelist.item( i ) );
916 }
917
918 return new UserLayer( name, lfc, styles, remoteOWS );
919 }
920
921 /**
922 * Creates a <tt>FeatureTypeConstraint</tt>-instance according to the contents of the DOM-subtree starting at the
923 * given 'FeatureTypeConstraint'-<tt>Element</tt>.
924 * <p>
925 *
926 * @param element
927 * the 'FeatureTypeConstraint'-<tt>Element</tt>
928 * @throws XMLParsingException
929 * if a syntactic or semantic error in the DOM-subtree is encountered
930 * @return the constructed <tt>FeatureTypeConstraint</tt>-instance
931 */
932 private static FeatureTypeConstraint createFeatureTypeConstraint( Element element )
933 throws XMLParsingException {
934 // optional: <Name>
935 Node node = XMLTools.getNode( element, "sld:FeatureTypeName/text()", CommonNamespaces.getNamespaceContext() );
936
937 QualifiedName name;
938 if ( node == null ) {
939 name = null;
940 } else {
941 name = XMLTools.getQualifiedNameValue( node );
942 }
943
944 // optional: <Filter>
945 Filter filter = null;
946 Element filterElement = XMLTools.getChildElement( "Filter", ogcNS, element );
947
948 if ( filterElement != null ) {
949 filter = AbstractFilter.buildFromDOM( filterElement );
950 }
951
952 // optional: <Extent>(s)
953 ElementList nodelist = XMLTools.getChildElements( "Extent", CommonNamespaces.SLDNS, element );
954 Extent[] extents = new Extent[nodelist.getLength()];
955
956 for ( int i = 0; i < nodelist.getLength(); i++ ) {
957 extents[i] = createExtent( nodelist.item( i ) );
958 }
959
960 return new FeatureTypeConstraint( name, filter, extents );
961 }
962
963 /**
964 * Creates an <tt>Extent</tt>-instance according to the contents of the DOM-subtree starting at the given 'Extent'-
965 * <tt>Element</tt>.
966 * <p>
967 *
968 * @param element
969 * the 'Extent'-<tt>Element</tt>
970 * @throws XMLParsingException
971 * if a syntactic or semantic error in the DOM-subtree is encountered
972 * @return the constructed <tt>Extent</tt>-instance
973 */
974 private static Extent createExtent( Element element )
975 throws XMLParsingException {
976 // required: <Name>
977 String name = XMLTools.getRequiredStringValue( "Name", CommonNamespaces.SLDNS, element );
978 // required: <Value>
979 String value = XMLTools.getRequiredStringValue( "Value", CommonNamespaces.SLDNS, element );
980
981 return new Extent( name, value );
982 }
983
984 /**
985 * Creates a <tt>LayerFeatureConstraints</tt>-instance according to the contents of the DOM-subtree starting at the
986 * given 'LayerFeatureConstraints'-<tt>Element</tt>.
987 * <p>
988 *
989 * @param element
990 * the 'LayerFeatureConstraints'-<tt>Element</tt>
991 * @throws XMLParsingException
992 * if a syntactic or semantic error in the DOM-subtree is encountered
993 * @return the constructed <tt>LayerFeatureConstraints</tt>-instance
994 */
995 public static LayerFeatureConstraints createLayerFeatureConstraints( Element element )
996 throws XMLParsingException {
997 // required: <FeatureTypeConstraint>(s)
998 ElementList nodelist = XMLTools.getChildElements( "FeatureTypeConstraint", CommonNamespaces.SLDNS, element );
999 FeatureTypeConstraint[] ftcs = new FeatureTypeConstraint[nodelist.getLength()];
1000
1001 for ( int i = 0; i < nodelist.getLength(); i++ ) {
1002 ftcs[i] = createFeatureTypeConstraint( nodelist.item( i ) );
1003 }
1004
1005 return new LayerFeatureConstraints( ftcs );
1006 }
1007
1008 /**
1009 * Creates a <tt>UserStyle</tt>-instance according to the contents of the DOM-subtree starting at the given
1010 * 'UserStyle'-<tt>Element</tt>.
1011 * <p>
1012 *
1013 * @param element
1014 * the 'UserStyle'-<tt>Element</tt>
1015 * @throws XMLParsingException
1016 * if a syntactic or semantic error in the DOM-subtree is encountered
1017 * @return the constructed <tt>UserStyle</tt>-instance
1018 */
1019 public static UserStyle createUserStyle( Element element )
1020 throws XMLParsingException {
1021 // optional: <Name>
1022 String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
1023 // optional: <Title>
1024 String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
1025 // optional: <Abstract>
1026 String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
1027
1028 // optional: <IsDefault>
1029 String defaultString = XMLTools.getStringValue( "IsDefault", CommonNamespaces.SLDNS, element, null );
1030 boolean isDefault = false;
1031
1032 if ( defaultString != null ) {
1033 if ( defaultString.equals( "1" ) ) {
1034 isDefault = true;
1035 }
1036 }
1037
1038 // required: <FeatureTypeStyle> (s)
1039 ElementList nl = XMLTools.getChildElements( "FeatureTypeStyle", CommonNamespaces.SLDNS, element );
1040 FeatureTypeStyle[] styles = new FeatureTypeStyle[nl.getLength()];
1041
1042 if ( styles.length == 0 ) {
1043 throw new XMLParsingException( "Required child-element 'FeatureTypeStyle' of element "
1044 + "'UserStyle' is missing!" );
1045 }
1046
1047 for ( int i = 0; i < nl.getLength(); i++ ) {
1048 styles[i] = createFeatureTypeStyle( nl.item( i ) );
1049 }
1050
1051 return new UserStyle( name, title, abstract_, isDefault, styles );
1052 }
1053
1054 /**
1055 * Creates a <tt>FeatureTypeStyle</tt>-instance according to the contents of the DOM-subtree starting at the given
1056 * 'FeatureTypeStyle'-<tt>Element</tt>.
1057 * <p>
1058 * TODO: The ElseFilter currently does not work correctly with FeatureFilters.
1059 * <p>
1060 *
1061 * @param element
1062 * the 'FeatureTypeStyle'-<tt>Element</tt>
1063 * @throws XMLParsingException
1064 * if a syntactic or semantic error in the DOM-subtree is encountered
1065 * @return the constructed <tt>FeatureTypeStyle</tt>-instance
1066 */
1067 public static FeatureTypeStyle createFeatureTypeStyle( Element element )
1068 throws XMLParsingException {
1069 // optional: <Name>
1070 String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
1071 // optional: <Title>
1072 String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
1073 // optional: <Abstract>
1074 String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
1075 // optional: <FeatureTypeName>
1076 String featureTypeName = XMLTools.getStringValue( "FeatureTypeName", CommonNamespaces.SLDNS, element, null );
1077
1078 // optional: several <Rule> / <SemanticTypeIdentifier>
1079 NodeList nodelist = element.getChildNodes();
1080 ArrayList<Rule> ruleList = new ArrayList<Rule>();
1081 ArrayList<String> typeIdentifierList = new ArrayList<String>();
1082
1083 // collect Filters of all Rules
1084 ArrayList<Filter> filters = new ArrayList<Filter>();
1085 // collect all Rules that have an ElseFilter
1086 ArrayList<Rule> elseRules = new ArrayList<Rule>();
1087
1088 for ( int i = 0; i < nodelist.getLength(); i++ ) {
1089 if ( nodelist.item( i ) instanceof Element ) {
1090 Element child = (Element) nodelist.item( i );
1091 String namespace = child.getNamespaceURI();
1092 if ( !CommonNamespaces.SLDNS.toString().equals( namespace ) ) {
1093 continue;
1094 }
1095
1096 String childName = child.getLocalName();
1097
1098 if ( childName.equals( "Rule" ) ) {
1099 Rule rule = createRule( child );
1100 if ( rule.hasElseFilter() ) {
1101 elseRules.add( rule );
1102 } else if ( rule.getFilter() == null || rule.getFilter() instanceof ComplexFilter ) {
1103 filters.add( rule.getFilter() );
1104 }
1105 ruleList.add( rule );
1106 } else if ( childName.equals( "SemanticTypeIdentifier" ) ) {
1107 typeIdentifierList.add( XMLTools.getStringValue( child ) );
1108 }
1109 }
1110 }
1111
1112 // compute and set the ElseFilter for all ElseFilter-Rules
1113 Filter elseFilter = null;
1114 // a Rule exists with no Filter at all -> elseFilter = false
1115 if ( filters.contains( null ) ) {
1116 elseFilter = new FalseFilter();
1117 // one Rule with a Filter exists -> elseFilter = NOT Filter
1118 } else if ( filters.size() == 1 ) {
1119 elseFilter = new ComplexFilter( OperationDefines.NOT );
1120 List<Operation> arguments = ( (LogicalOperation) ( (ComplexFilter) elseFilter ).getOperation() ).getArguments();
1121 ComplexFilter complexFilter = (ComplexFilter) filters.get( 0 );
1122 arguments.add( complexFilter.getOperation() );
1123 // several Rules with Filters exist -> elseFilter = NOT (Filter1 OR Filter2 OR...)
1124 } else if ( filters.size() > 1 ) {
1125 ComplexFilter innerFilter = new ComplexFilter( OperationDefines.OR );
1126 elseFilter = new ComplexFilter( innerFilter, null, OperationDefines.NOT );
1127 List<Operation> arguments = ( (LogicalOperation) innerFilter.getOperation() ).getArguments();
1128 Iterator<Filter> it = filters.iterator();
1129 while ( it.hasNext() ) {
1130 ComplexFilter complexFilter = (ComplexFilter) it.next();
1131 arguments.add( complexFilter.getOperation() );
1132 }
1133 }
1134 Iterator<Rule> it = elseRules.iterator();
1135 while ( it.hasNext() ) {
1136 it.next().setFilter( elseFilter );
1137 }
1138
1139 Rule[] rules = ruleList.toArray( new Rule[ruleList.size()] );
1140 String[] typeIdentifiers = typeIdentifierList.toArray( new String[typeIdentifierList.size()] );
1141
1142 return new FeatureTypeStyle( name, title, abstract_, featureTypeName, typeIdentifiers, rules );
1143 }
1144
1145 /**
1146 * Creates a <tt>Rule</tt>-instance according to the contents of the DOM-subtree starting at the given 'Rule'-
1147 * <tt>Element</tt>.
1148 * <p>
1149 *
1150 * @param element
1151 * the 'Rule'-<tt>Element</tt>
1152 * @throws XMLParsingException
1153 * if a syntactic or semantic error in the DOM-subtree is encountered
1154 * @return the constructed <tt>Rule</tt>-instance
1155 */
1156 private static Rule createRule( Element element )
1157 throws XMLParsingException {
1158 // optional: <Name>
1159 String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
1160 // optional: <Title>
1161 String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
1162 // optional: <Abstract>
1163 String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
1164
1165 // optional: <LegendGraphic>
1166 LegendGraphic legendGraphic = null;
1167 Element legendGraphicElement = XMLTools.getChildElement( "LegendGraphic", CommonNamespaces.SLDNS, element );
1168
1169 if ( legendGraphicElement != null ) {
1170 legendGraphic = createLegendGraphic( legendGraphicElement );
1171 }
1172
1173 // optional: <Filter>
1174 boolean isAnElseFilter = false;
1175 Filter filter = null;
1176 Element filterElement = XMLTools.getChildElement( "Filter", ogcNS, element );
1177 if ( filterElement != null ) {
1178 filter = AbstractFilter.buildFromDOM( filterElement );
1179 }
1180
1181 // optional: <ElseFilter>
1182 Element elseFilterElement = XMLTools.getChildElement( "ElseFilter", CommonNamespaces.SLDNS, element );
1183 if ( elseFilterElement != null ) {
1184 isAnElseFilter = true;
1185 }
1186
1187 if ( ( filterElement != null ) && ( elseFilterElement != null ) ) {
1188 throw new XMLParsingException( "Element 'Rule' may contain a 'Filter'- or "
1189 + "an 'ElseFilter'-element, but not both!" );
1190 }
1191
1192 // optional: <MinScaleDenominator>
1193 double min = XMLTools.getNodeAsDouble( element, "sld:MinScaleDenominator", nsContext, 0.0 );
1194 // optional: <MaxScaleDenominator>
1195 double max = XMLTools.getNodeAsDouble( element, "sld:MaxScaleDenominator", nsContext, 9E99 );
1196
1197 // optional: different Symbolizer-elements
1198 NodeList symbolizerNL = element.getChildNodes();
1199 ArrayList<Symbolizer> symbolizerList = new ArrayList<Symbolizer>( symbolizerNL.getLength() );
1200
1201 for ( int i = 0; i < symbolizerNL.getLength(); i++ ) {
1202 if ( symbolizerNL.item( i ) instanceof Element ) {
1203 Element symbolizerElement = (Element) symbolizerNL.item( i );
1204 String namespace = symbolizerElement.getNamespaceURI();
1205
1206 if ( !CommonNamespaces.SLDNS.toString().equals( namespace )
1207 && !CommonNamespaces.SENS.toString().equals( namespace ) ) {
1208 continue;
1209 }
1210
1211 String symbolizerName = symbolizerElement.getLocalName();
1212
1213 if ( symbolizerName.equals( "LineSymbolizer" ) ) {
1214 symbolizerList.add( createLineSymbolizer( symbolizerElement, min, max ) );
1215 } else if ( symbolizerName.equals( "PointSymbolizer" ) ) {
1216 symbolizerList.add( createPointSymbolizer( symbolizerElement, min, max ) );
1217 } else if ( symbolizerName.equals( "PolygonSymbolizer" ) ) {
1218 symbolizerList.add( createPolygonSymbolizer( symbolizerElement, min, max ) );
1219 } else if ( symbolizerName.equals( "TextSymbolizer" ) ) {
1220 symbolizerList.add( createTextSymbolizer( symbolizerElement, min, max ) );
1221 } else if ( symbolizerName.equals( "RasterSymbolizer" ) ) {
1222 symbolizerList.add( createRasterSymbolizer( symbolizerElement, min, max ) );
1223 }
1224 }
1225 }
1226
1227 Symbolizer[] symbolizers = symbolizerList.toArray( new Symbolizer[symbolizerList.size()] );
1228
1229 return new Rule( symbolizers, name, title, abstract_, legendGraphic, filter, isAnElseFilter, min, max );
1230 }
1231
1232 /**
1233 *
1234 * @param symbolizerElement
1235 * @return {@link Symbolizer}
1236 * @throws XMLParsingException
1237 */
1238 public static Symbolizer createSymbolizer( Element symbolizerElement ) throws XMLParsingException {
1239 String symbolizerName = symbolizerElement.getLocalName();
1240
1241 double min = 0;
1242 double max = Double.MAX_VALUE;
1243 Symbolizer symbolizer = null;
1244 if ( symbolizerName.equals( "LineSymbolizer" ) ) {
1245 symbolizer = createLineSymbolizer( symbolizerElement, min, max );
1246 } else if ( symbolizerName.equals( "PointSymbolizer" ) ) {
1247 symbolizer = createPointSymbolizer( symbolizerElement, min, max );
1248 } else if ( symbolizerName.equals( "PolygonSymbolizer" ) ) {
1249 symbolizer = createPolygonSymbolizer( symbolizerElement, min, max );
1250 } else if ( symbolizerName.equals( "TextSymbolizer" ) ) {
1251 symbolizer = createTextSymbolizer( symbolizerElement, min, max );
1252 } else if ( symbolizerName.equals( "RasterSymbolizer" ) ) {
1253 symbolizer = createRasterSymbolizer( symbolizerElement, min, max );
1254 }
1255 return symbolizer;
1256 }
1257
1258 /**
1259 * Creates a <tt>PointSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
1260 * 'PointSymbolizer'-<tt>Element</tt>.
1261 * <p>
1262 *
1263 * @param element
1264 * the 'PointSymbolizer'-<tt>Element</tt>
1265 * @param min
1266 * scale-constraint to be used
1267 * @param max
1268 * scale-constraint to be used
1269 * @throws XMLParsingException
1270 * if a syntactic or semantic error in the DOM-subtree is encountered
1271 * @return the constructed <tt>PointSymbolizer</tt>-instance
1272 */
1273 private static PointSymbolizer createPointSymbolizer( Element element, double min, double max )
1274 throws XMLParsingException {
1275
1276 // optional: <Geometry>
1277 Geometry geometry = null;
1278 Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1279
1280 if ( geometryElement != null ) {
1281 geometry = createGeometry( geometryElement );
1282 }
1283
1284 // optional: <Graphic>
1285 Graphic graphic = null;
1286 Element graphicElement = XMLTools.getChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1287
1288 if ( graphicElement != null ) {
1289 graphic = createGraphic( graphicElement );
1290 }
1291
1292 PointSymbolizer ps = null;
1293 String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1294 if ( respClass == null ) {
1295 ps = new PointSymbolizer( graphic, geometry, min, max );
1296 } else {
1297 ps = new PointSymbolizer( graphic, geometry, respClass, min, max );
1298 }
1299
1300 return ps;
1301 }
1302
1303 /**
1304 * Creates a <tt>LineSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
1305 * 'LineSymbolizer'-<tt>Element</tt>.
1306 * <p>
1307 *
1308 * @param element
1309 * the 'LineSymbolizer'-<tt>Element</tt>
1310 * @param min
1311 * scale-constraint to be used
1312 * @param max
1313 * scale-constraint to be used
1314 * @throws XMLParsingException
1315 * if a syntactic or semantic error in the DOM-subtree is encountered
1316 * @return the constructed <tt>LineSymbolizer</tt>-instance
1317 */
1318 private static LineSymbolizer createLineSymbolizer( Element element, double min, double max )
1319 throws XMLParsingException {
1320
1321 // optional: <Geometry>
1322 Geometry geometry = null;
1323 Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1324
1325 if ( geometryElement != null ) {
1326 geometry = createGeometry( geometryElement );
1327 }
1328
1329 // optional: <Stroke>
1330 Stroke stroke = null;
1331 Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1332
1333 if ( strokeElement != null ) {
1334 stroke = createStroke( strokeElement );
1335 }
1336
1337 LineSymbolizer ls = null;
1338 String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1339 if ( respClass == null ) {
1340 ls = new LineSymbolizer( stroke, geometry, min, max );
1341 } else {
1342 ls = new LineSymbolizer( stroke, geometry, respClass, min, max );
1343 }
1344 return ls;
1345 }
1346
1347 /**
1348 * Creates a <tt>PolygonSymbolizer</tt>-instance according to the contents of the DOM-subtree starting at the given
1349 * 'PolygonSymbolizer'-<tt>Element</tt>.
1350 * <p>
1351 *
1352 * @param element
1353 * the 'PolygonSymbolizer'-<tt>Element</tt>
1354 * @param min
1355 * scale-constraint to be used
1356 * @param max
1357 * scale-constraint to be used
1358 * @throws XMLParsingException
1359 * if a syntactic or semantic error in the DOM-subtree is encountered
1360 * @return the constructed <tt>PolygonSymbolizer</tt>-instance
1361 */
1362 private static PolygonSymbolizer createPolygonSymbolizer( Element element, double min, double max )
1363 throws XMLParsingException {
1364 // optional: <Geometry>
1365 Geometry geometry = null;
1366 Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1367
1368 if ( geometryElement != null ) {
1369 geometry = createGeometry( geometryElement );
1370 }
1371
1372 // optional: <Fill>
1373 Fill fill = null;
1374 Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
1375
1376 if ( fillElement != null ) {
1377 fill = createFill( fillElement );
1378 }
1379
1380 // optional: <Stroke>
1381 Stroke stroke = null;
1382 Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1383
1384 if ( strokeElement != null ) {
1385 stroke = createStroke( strokeElement );
1386 }
1387
1388 PolygonSymbolizer ps = null;
1389 String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1390 if ( respClass == null ) {
1391 ps = new PolygonSymbolizer( fill, stroke, geometry, min, max );
1392 } else {
1393 ps = new PolygonSymbolizer( fill, stroke, geometry, respClass, min, max );
1394 }
1395
1396 return ps;
1397 }
1398
1399 /**
1400 * Creates a <tt>Geometry</tt>-instance according to the contents of the DOM-subtree starting at the given
1401 * 'Geometry'-<tt>Element</tt>.
1402 * <p>
1403 * FIXME: Add support for 'Function'-Elements.
1404 * <p>
1405 *
1406 * @param element
1407 * the 'Geometry'-<tt>Element</tt>
1408 * @throws XMLParsingException
1409 * if a syntactic or semantic error in the DOM-subtree is encountered
1410 * @return the constructed <tt>Geometry</tt>-instance
1411 */
1412 private static Geometry createGeometry( Element element )
1413 throws XMLParsingException {
1414 Geometry geometry = null;
1415
1416 // required: <PropertyName>
1417 Element propertyNameElement = XMLTools.getRequiredChildElement( "PropertyName", ogcNS, element );
1418
1419 // optional: <Function>
1420 Element functionElement = XMLTools.getChildElement( "Function", ogcNS, propertyNameElement );
1421
1422 // just a property name exists
1423 if ( functionElement == null ) {
1424 Node node = XMLTools.getNode( propertyNameElement, "/text()", nsContext );
1425 PropertyPath pp = OGCDocument.parsePropertyPath( (Text) node );
1426 geometry = new Geometry( pp, null );
1427 } else {
1428 // FIXME:
1429 // the property probably contains a wfs:Function expression
1430 }
1431
1432 return geometry;
1433 }
1434
1435 /**
1436 * Creates a <tt>Fill</tt>-instance according to the contents of the DOM-subtree starting at the given 'Fill'-
1437 * <tt>Element</tt>.
1438 * <p>
1439 *
1440 * @param element
1441 * the 'Fill'-<tt>Element</tt>
1442 * @throws XMLParsingException
1443 * if a syntactic or semantic error in the DOM-subtree is encountered
1444 * @return the constructed <tt>Fill</tt>-instance
1445 */
1446 private static Fill createFill( Element element )
1447 throws XMLParsingException {
1448 // optional: <GraphicFill>
1449 GraphicFill graphicFill = null;
1450 Element graphicFillElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
1451
1452 if ( graphicFillElement != null ) {
1453 graphicFill = createGraphicFill( graphicFillElement );
1454 }
1455
1456 // optional: <CssParameter>s
1457 ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
1458 HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
1459
1460 for ( int i = 0; i < nl.getLength(); i++ ) {
1461 CssParameter cssParam = createCssParameter( nl.item( i ) );
1462 cssParams.put( cssParam.getName(), cssParam );
1463 }
1464
1465 return new Fill( cssParams, graphicFill );
1466 }
1467
1468 /**
1469 * Creates a <tt>LegendGraphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1470 * 'LegendGraphic'-element.
1471 * <p>
1472 *
1473 * @param element
1474 * the 'LegendGraphic'-<tt>Element</tt>
1475 * @throws XMLParsingException
1476 * if a syntactic or semantic error in the DOM-subtree is encountered
1477 * @return the constructed <tt>Graphic</tt>-instance
1478 */
1479 private static LegendGraphic createLegendGraphic( Element element )
1480 throws XMLParsingException {
1481 // required: <Graphic>
1482 Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1483 Graphic graphic = createGraphic( graphicElement );
1484
1485 return new LegendGraphic( graphic );
1486 }
1487
1488 /**
1489 * Creates an <tt>ExternalGraphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1490 * 'ExternalGraphic'-<tt>Element</tt>.
1491 * <p>
1492 *
1493 * @param element
1494 * the 'ExternalGraphic'-<tt>Element</tt>
1495 * @throws XMLParsingException
1496 * if a syntactic or semantic error in the DOM-subtree is encountered
1497 * @return the constructed <tt>ExternalGraphic</tt>-instance
1498 */
1499 private static ExternalGraphic createExternalGraphic( Element element )
1500 throws XMLParsingException {
1501 // required: <OnlineResource>
1502 Element onlineResourceElement = XMLTools.getRequiredChildElement( "OnlineResource", CommonNamespaces.SLDNS,
1503 element );
1504
1505 // required: href-Attribute (in <OnlineResource>)
1506 String href = XMLTools.getRequiredAttrValue( "href", xlnNS, onlineResourceElement );
1507 URL url = null;
1508 try {
1509 url = sldDoc.resolve( href );
1510 } catch ( MalformedURLException e ) {
1511 LOG.logDebug( e.getMessage(), e );
1512 throw new XMLParsingException( "Value ('" + href + "') of attribute 'href' of "
1513 + "element 'OnlineResoure' does not denote a valid URL" );
1514 }
1515
1516 // required: <Format> (in <OnlineResource>)
1517 String format = XMLTools.getRequiredStringValue( "Format", CommonNamespaces.SLDNS, element );
1518
1519 return new ExternalGraphic( format, url );
1520 }
1521
1522 /**
1523 * Creates a <tt>Mark</tt>-instance according to the contents of the DOM-subtree starting at the given 'Mark'-
1524 * <tt>Element</tt>.
1525 * <p>
1526 *
1527 * @param element
1528 * the 'Mark'-<tt>Element</tt>
1529 * @throws XMLParsingException
1530 * if a syntactic or semantic error in the DOM-subtree is encountered
1531 * @return the constructed <tt>Mark</tt>-instance
1532 */
1533 private static Mark createMark( Element element )
1534 throws XMLParsingException {
1535 Stroke stroke = null;
1536 Fill fill = null;
1537
1538 // optional: <WellKnownName>
1539 String wkn = XMLTools.getStringValue( "WellKnownName", CommonNamespaces.SLDNS, element, null );
1540
1541 // optional: <Stroke>
1542 Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1543
1544 if ( strokeElement != null ) {
1545 stroke = createStroke( strokeElement );
1546 }
1547
1548 // optional: <Fill>
1549 Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
1550
1551 if ( fillElement != null ) {
1552 fill = createFill( fillElement );
1553 }
1554
1555 return new Mark( wkn, stroke, fill );
1556 }
1557
1558 /**
1559 * Creates a <tt>Stroke</tt>-instance according to the contents of the DOM-subtree starting at the given 'Stroke'-
1560 * <tt>Element</tt>.
1561 * <p>
1562 *
1563 * @param element
1564 * the 'Stroke'-<tt>Element</tt>
1565 * @throws XMLParsingException
1566 * if a syntactic or semantic error in the DOM-subtree is encountered
1567 * @return the constructed <tt>Stroke</tt>-instance
1568 */
1569 private static Stroke createStroke( Element element )
1570 throws XMLParsingException {
1571 GraphicFill gf = null;
1572 GraphicStroke gs = null;
1573
1574 // optional: <GraphicFill>
1575 Element gfElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
1576
1577 if ( gfElement != null ) {
1578 gf = createGraphicFill( gfElement );
1579 }
1580
1581 // optional: <GraphicStroke>
1582 Element gsElement = XMLTools.getChildElement( "GraphicStroke", CommonNamespaces.SLDNS, element );
1583
1584 if ( gsElement != null ) {
1585 gs = createGraphicStroke( gsElement );
1586 }
1587
1588 // optional: <CssParameter>s
1589 ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
1590 HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
1591
1592 for ( int i = 0; i < nl.getLength(); i++ ) {
1593 CssParameter cssParam = createCssParameter( nl.item( i ) );
1594 cssParams.put( cssParam.getName(), cssParam );
1595 }
1596
1597 return new Stroke( cssParams, gs, gf );
1598 }
1599
1600 /**
1601 * Creates a <tt>GraphicFill</tt>-instance according to the contents of the DOM-subtree starting at the given
1602 * 'GraphicFill'-<tt>Element</tt>.
1603 * <p>
1604 *
1605 * @param element
1606 * the 'GraphicFill'-<tt>Element</tt>
1607 * @throws XMLParsingException
1608 * if a syntactic or semantic error in the DOM-subtree is encountered
1609 * @return the constructed <tt>GraphicFill</tt>-instance
1610 */
1611 private static GraphicFill createGraphicFill( Element element )
1612 throws XMLParsingException {
1613 // required: <Graphic>
1614 Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1615 Graphic graphic = createGraphic( graphicElement );
1616
1617 return new GraphicFill( graphic );
1618 }
1619
1620 /**
1621 * Creates a <tt>GraphicStroke</tt>-instance according to the contents of the DOM-subtree starting at the given
1622 * 'GraphicStroke'-<tt>Element</tt>.
1623 * <p>
1624 *
1625 * @param element
1626 * the 'GraphicStroke'-<tt>Element</tt>
1627 * @throws XMLParsingException
1628 * if a syntactic or semantic error in the DOM-subtree is encountered
1629 * @return the constructed <tt>GraphicStroke</tt>-instance
1630 */
1631 private static GraphicStroke createGraphicStroke( Element element )
1632 throws XMLParsingException {
1633 // required: <Graphic>
1634 Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1635 Graphic graphic = createGraphic( graphicElement );
1636
1637 return new GraphicStroke( graphic );
1638 }
1639
1640 /**
1641 * Creates a <tt>Graphic</tt>-instance according to the contents of the DOM-subtree starting at the given
1642 * 'Graphic'-element.
1643 * <p>
1644 *
1645 * @param element
1646 * the 'Graphic'-<tt>Element</tt>
1647 * @throws XMLParsingException
1648 * if a syntactic or semantic error in the DOM-subtree is encountered
1649 * @return the constructed <tt>Graphic</tt>-instance
1650 */
1651 private static Graphic createGraphic( Element element )
1652 throws XMLParsingException {
1653
1654 // optional: <Opacity>
1655 ParameterValueType opacity = null;
1656 // optional: <Size>
1657 ParameterValueType size = null;
1658 // optional: <Rotation>
1659 ParameterValueType rotation = null;
1660
1661 // optional: <ExternalGraphic>s / <Mark>s
1662 NodeList nodelist = element.getChildNodes();
1663 ArrayList<Object> marksAndExtGraphicsList = new ArrayList<Object>();
1664
1665 for ( int i = 0; i < nodelist.getLength(); i++ ) {
1666 if ( nodelist.item( i ) instanceof Element ) {
1667 Element child = (Element) nodelist.item( i );
1668 String namespace = child.getNamespaceURI();
1669
1670 if ( !CommonNamespaces.SLDNS.toString().equals( namespace ) ) {
1671 continue;
1672 }
1673
1674 String childName = child.getLocalName();
1675
1676 if ( childName.equals( "ExternalGraphic" ) ) {
1677 marksAndExtGraphicsList.add( createExternalGraphic( child ) );
1678 } else if ( childName.equals( "Mark" ) ) {
1679 marksAndExtGraphicsList.add( createMark( child ) );
1680 } else if ( childName.equals( "Opacity" ) ) {
1681 opacity = createParameterValueType( child );
1682 } else if ( childName.equals( "Size" ) ) {
1683 size = createParameterValueType( child );
1684 } else if ( childName.equals( "Rotation" ) ) {
1685 rotation = createParameterValueType( child );
1686 }
1687 }
1688 }
1689
1690 Object[] marksAndExtGraphics = marksAndExtGraphicsList.toArray( new Object[marksAndExtGraphicsList.size()] );
1691
1692 return new Graphic( marksAndExtGraphics, opacity, size, rotation );
1693 }
1694
1695 /**
1696 * Creates a <tt>CssParameter</tt>-instance according to the contents of the DOM-subtree starting at the given
1697 * 'CssParameter'-<tt>Element</tt>.
1698 * <p>
1699 *
1700 * @param element
1701 * the 'CssParamter'-<tt>Element</tt>
1702 * @throws XMLParsingException
1703 * if a syntactic or semantic error in the DOM-subtree is encountered
1704 * @return the constructed <tt>CssParameter</tt>-instance
1705 */
1706 private static CssParameter createCssParameter( Element element )
1707 throws XMLParsingException {
1708 // required: name-Attribute
1709 String name = XMLTools.getRequiredAttrValue( "name", null, element );
1710 ParameterValueType pvt = createParameterValueType( element );
1711
1712 return ( new CssParameter( name, pvt ) );
1713 }
1714
1715 /**
1716 * <code>TresholdsBelongTo</code> enumerates values possibly belonging to <code>ThreshholdsBelongToType</code>.
1717 *
1718 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1719 * @author last edited by: $Author: mschneider $
1720 *
1721 * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
1722 */
1723 public enum ThresholdsBelongTo {
1724 /**
1725 * <code>"succeeding"</code>
1726 */
1727 SUCCEEDING,
1728 /**
1729 * <code>"preceding"</code>
1730 */
1731 PRECEDING
1732 }
1733
1734 /**
1735 * <code>Mode</code> is the ModeType from the Symbology Encoding Schema.
1736 *
1737 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1738 * @author last edited by: $Author: mschneider $
1739 *
1740 * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
1741 */
1742 public enum Mode {
1743 /**
1744 * <code>"linear"</code>
1745 */
1746 LINEAR,
1747 /**
1748 * <code>"cosine"</code>
1749 */
1750 COSINE,
1751 /**
1752 * <code>"cubic"</code>
1753 */
1754 CUBIC
1755 }
1756
1757 /**
1758 * <code>Method</code> is the MethodType from the Symbology encoding Schema.
1759 *
1760 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1761 * @author last edited by: $Author: mschneider $
1762 *
1763 * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
1764 */
1765 public enum Method {
1766 /**
1767 * <code>"numeric"</code>
1768 */
1769 NUMERIC,
1770 /**
1771 * <code>"color"</code>
1772 */
1773 COLOR
1774 }
1775
1776 }