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