001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/sld/SLDFactory.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2008 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: 12138 $ $Date: 2008-06-04 10:34:15 +0200 (Mi, 04 Jun 2008) $
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.getNode( element, "sld:FeatureTypeName/text()", CommonNamespaces.getNamespaceContext() );
899
900 QualifiedName name;
901 if ( node == null ) {
902 name = null;
903 } else {
904 name = XMLTools.getQualifiedNameValue( node );
905 }
906
907 // optional: <Filter>
908 Filter filter = null;
909 Element filterElement = XMLTools.getChildElement( "Filter", ogcNS, element );
910
911 if ( filterElement != null ) {
912 filter = AbstractFilter.buildFromDOM( filterElement );
913 }
914
915 // optional: <Extent>(s)
916 ElementList nodelist = XMLTools.getChildElements( "Extent", CommonNamespaces.SLDNS, element );
917 Extent[] extents = new Extent[nodelist.getLength()];
918
919 for ( int i = 0; i < nodelist.getLength(); i++ ) {
920 extents[i] = createExtent( nodelist.item( i ) );
921 }
922
923 return new FeatureTypeConstraint( name, filter, extents );
924 }
925
926 /**
927 * Creates an <tt>Extent</tt>-instance according to the contents of the DOM-subtree starting
928 * at the given 'Extent'-<tt>Element</tt>.
929 * <p>
930 *
931 * @param element
932 * the 'Extent'-<tt>Element</tt>
933 * @throws XMLParsingException
934 * if a syntactic or semantic error in the DOM-subtree is encountered
935 * @return the constructed <tt>Extent</tt>-instance
936 */
937 private static Extent createExtent( Element element )
938 throws XMLParsingException {
939 // required: <Name>
940 String name = XMLTools.getRequiredStringValue( "Name", CommonNamespaces.SLDNS, element );
941 // required: <Value>
942 String value = XMLTools.getRequiredStringValue( "Value", CommonNamespaces.SLDNS, element );
943
944 return new Extent( name, value );
945 }
946
947 /**
948 * Creates a <tt>LayerFeatureConstraints</tt>-instance according to the contents of the
949 * DOM-subtree starting at the given 'LayerFeatureConstraints'-<tt>Element</tt>.
950 * <p>
951 *
952 * @param element
953 * the 'LayerFeatureConstraints'-<tt>Element</tt>
954 * @throws XMLParsingException
955 * if a syntactic or semantic error in the DOM-subtree is encountered
956 * @return the constructed <tt>LayerFeatureConstraints</tt>-instance
957 */
958 public static LayerFeatureConstraints createLayerFeatureConstraints( Element element )
959 throws XMLParsingException {
960 // required: <FeatureTypeConstraint>(s)
961 ElementList nodelist = XMLTools.getChildElements( "FeatureTypeConstraint", CommonNamespaces.SLDNS, element );
962 FeatureTypeConstraint[] ftcs = new FeatureTypeConstraint[nodelist.getLength()];
963
964 for ( int i = 0; i < nodelist.getLength(); i++ ) {
965 ftcs[i] = createFeatureTypeConstraint( nodelist.item( i ) );
966 }
967
968 return new LayerFeatureConstraints( ftcs );
969 }
970
971 /**
972 * Creates a <tt>UserStyle</tt>-instance according to the contents of the DOM-subtree
973 * starting at the given 'UserStyle'-<tt>Element</tt>.
974 * <p>
975 *
976 * @param element
977 * the 'UserStyle'-<tt>Element</tt>
978 * @throws XMLParsingException
979 * if a syntactic or semantic error in the DOM-subtree is encountered
980 * @return the constructed <tt>UserStyle</tt>-instance
981 */
982 private static UserStyle createUserStyle( Element element )
983 throws XMLParsingException {
984 // optional: <Name>
985 String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
986 // optional: <Title>
987 String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
988 // optional: <Abstract>
989 String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
990
991 // optional: <IsDefault>
992 String defaultString = XMLTools.getStringValue( "IsDefault", CommonNamespaces.SLDNS, element, null );
993 boolean isDefault = false;
994
995 if ( defaultString != null ) {
996 if ( defaultString.equals( "1" ) ) {
997 isDefault = true;
998 }
999 }
1000
1001 // required: <FeatureTypeStyle> (s)
1002 ElementList nl = XMLTools.getChildElements( "FeatureTypeStyle", CommonNamespaces.SLDNS, element );
1003 FeatureTypeStyle[] styles = new FeatureTypeStyle[nl.getLength()];
1004
1005 if ( styles.length == 0 ) {
1006 throw new XMLParsingException( "Required child-element 'FeatureTypeStyle' of element "
1007 + "'UserStyle' is missing!" );
1008 }
1009
1010 for ( int i = 0; i < nl.getLength(); i++ ) {
1011 styles[i] = createFeatureTypeStyle( nl.item( i ) );
1012 }
1013
1014 return new UserStyle( name, title, abstract_, isDefault, styles );
1015 }
1016
1017 /**
1018 * Creates a <tt>FeatureTypeStyle</tt>-instance according to the contents of the DOM-subtree
1019 * starting at the given 'FeatureTypeStyle'-<tt>Element</tt>.
1020 * <p>
1021 * TODO: The ElseFilter currently does not work correctly with FeatureFilters.
1022 * <p>
1023 *
1024 * @param element
1025 * the 'FeatureTypeStyle'-<tt>Element</tt>
1026 * @throws XMLParsingException
1027 * if a syntactic or semantic error in the DOM-subtree is encountered
1028 * @return the constructed <tt>FeatureTypeStyle</tt>-instance
1029 */
1030 public static FeatureTypeStyle createFeatureTypeStyle( Element element )
1031 throws XMLParsingException {
1032 // optional: <Name>
1033 String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
1034 // optional: <Title>
1035 String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
1036 // optional: <Abstract>
1037 String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
1038 // optional: <FeatureTypeName>
1039 String featureTypeName = XMLTools.getStringValue( "FeatureTypeName", CommonNamespaces.SLDNS, element, null );
1040
1041 // optional: several <Rule> / <SemanticTypeIdentifier>
1042 NodeList nodelist = element.getChildNodes();
1043 ArrayList<Rule> ruleList = new ArrayList<Rule>();
1044 ArrayList<String> typeIdentifierList = new ArrayList<String>();
1045
1046 // collect Filters of all Rules
1047 ArrayList<Filter> filters = new ArrayList<Filter>();
1048 // collect all Rules that have an ElseFilter
1049 ArrayList<Rule> elseRules = new ArrayList<Rule>();
1050
1051 for ( int i = 0; i < nodelist.getLength(); i++ ) {
1052 if ( nodelist.item( i ) instanceof Element ) {
1053 Element child = (Element) nodelist.item( i );
1054 String namespace = child.getNamespaceURI();
1055 if ( !CommonNamespaces.SLDNS.toString().equals( namespace ) ) {
1056 continue;
1057 }
1058
1059 String childName = child.getLocalName();
1060
1061 if ( childName.equals( "Rule" ) ) {
1062 Rule rule = createRule( child );
1063 if ( rule.hasElseFilter() ) {
1064 elseRules.add( rule );
1065 } else if ( rule.getFilter() == null || rule.getFilter() instanceof ComplexFilter ) {
1066 filters.add( rule.getFilter() );
1067 }
1068 ruleList.add( rule );
1069 } else if ( childName.equals( "SemanticTypeIdentifier" ) ) {
1070 typeIdentifierList.add( XMLTools.getStringValue( child ) );
1071 }
1072 }
1073 }
1074
1075 // compute and set the ElseFilter for all ElseFilter-Rules
1076 Filter elseFilter = null;
1077 // a Rule exists with no Filter at all -> elseFilter = false
1078 if ( filters.contains( null ) ) {
1079 elseFilter = new FalseFilter();
1080 // one Rule with a Filter exists -> elseFilter = NOT Filter
1081 } else if ( filters.size() == 1 ) {
1082 elseFilter = new ComplexFilter( OperationDefines.NOT );
1083 List<Operation> arguments = ( (LogicalOperation) ( (ComplexFilter) elseFilter ).getOperation() ).getArguments();
1084 ComplexFilter complexFilter = (ComplexFilter) filters.get( 0 );
1085 arguments.add( complexFilter.getOperation() );
1086 // several Rules with Filters exist -> elseFilter = NOT (Filter1 OR Filter2 OR...)
1087 } else if ( filters.size() > 1 ) {
1088 ComplexFilter innerFilter = new ComplexFilter( OperationDefines.OR );
1089 elseFilter = new ComplexFilter( innerFilter, null, OperationDefines.NOT );
1090 List<Operation> arguments = ( (LogicalOperation) innerFilter.getOperation() ).getArguments();
1091 Iterator it = filters.iterator();
1092 while ( it.hasNext() ) {
1093 ComplexFilter complexFilter = (ComplexFilter) it.next();
1094 arguments.add( complexFilter.getOperation() );
1095 }
1096 }
1097 Iterator it = elseRules.iterator();
1098 while ( it.hasNext() ) {
1099 ( (Rule) it.next() ).setFilter( elseFilter );
1100 }
1101
1102 Rule[] rules = ruleList.toArray( new Rule[ruleList.size()] );
1103 String[] typeIdentifiers = typeIdentifierList.toArray( new String[typeIdentifierList.size()] );
1104
1105 return new FeatureTypeStyle( name, title, abstract_, featureTypeName, typeIdentifiers, rules );
1106 }
1107
1108 /**
1109 * Creates a <tt>Rule</tt>-instance according to the contents of the DOM-subtree starting at
1110 * the given 'Rule'-<tt>Element</tt>.
1111 * <p>
1112 *
1113 * @param element
1114 * the 'Rule'-<tt>Element</tt>
1115 * @throws XMLParsingException
1116 * if a syntactic or semantic error in the DOM-subtree is encountered
1117 * @return the constructed <tt>Rule</tt>-instance
1118 */
1119 private static Rule createRule( Element element )
1120 throws XMLParsingException {
1121 // optional: <Name>
1122 String name = XMLTools.getStringValue( "Name", CommonNamespaces.SLDNS, element, null );
1123 // optional: <Title>
1124 String title = XMLTools.getStringValue( "Title", CommonNamespaces.SLDNS, element, null );
1125 // optional: <Abstract>
1126 String abstract_ = XMLTools.getStringValue( "Abstract", CommonNamespaces.SLDNS, element, null );
1127
1128 // optional: <LegendGraphic>
1129 LegendGraphic legendGraphic = null;
1130 Element legendGraphicElement = XMLTools.getChildElement( "LegendGraphic", CommonNamespaces.SLDNS, element );
1131
1132 if ( legendGraphicElement != null ) {
1133 legendGraphic = createLegendGraphic( legendGraphicElement );
1134 }
1135
1136 // optional: <Filter>
1137 boolean isAnElseFilter = false;
1138 Filter filter = null;
1139 Element filterElement = XMLTools.getChildElement( "Filter", ogcNS, element );
1140 if ( filterElement != null ) {
1141 filter = AbstractFilter.buildFromDOM( filterElement );
1142 }
1143
1144 // optional: <ElseFilter>
1145 Element elseFilterElement = XMLTools.getChildElement( "ElseFilter", CommonNamespaces.SLDNS, element );
1146 if ( elseFilterElement != null ) {
1147 isAnElseFilter = true;
1148 }
1149
1150 if ( ( filterElement != null ) && ( elseFilterElement != null ) ) {
1151 throw new XMLParsingException( "Element 'Rule' may contain a 'Filter'- or "
1152 + "an 'ElseFilter'-element, but not both!" );
1153 }
1154
1155 // optional: <MinScaleDenominator>
1156 double min = XMLTools.getNodeAsDouble( element, "sld:MinScaleDenominator", nsContext, 0.0 );
1157 // optional: <MaxScaleDenominator>
1158 double max = XMLTools.getNodeAsDouble( element, "sld:MaxScaleDenominator", nsContext, 9E99 );
1159
1160 // optional: different Symbolizer-elements
1161 NodeList symbolizerNL = element.getChildNodes();
1162 ArrayList<Symbolizer> symbolizerList = new ArrayList<Symbolizer>( symbolizerNL.getLength() );
1163
1164 for ( int i = 0; i < symbolizerNL.getLength(); i++ ) {
1165 if ( symbolizerNL.item( i ) instanceof Element ) {
1166 Element symbolizerElement = (Element) symbolizerNL.item( i );
1167 String namespace = symbolizerElement.getNamespaceURI();
1168
1169 if ( !CommonNamespaces.SLDNS.toString().equals( namespace )
1170 && !CommonNamespaces.SENS.toString().equals( namespace ) ) {
1171 continue;
1172 }
1173
1174 String symbolizerName = symbolizerElement.getLocalName();
1175
1176 if ( symbolizerName.equals( "LineSymbolizer" ) ) {
1177 symbolizerList.add( createLineSymbolizer( symbolizerElement, min, max ) );
1178 } else if ( symbolizerName.equals( "PointSymbolizer" ) ) {
1179 symbolizerList.add( createPointSymbolizer( symbolizerElement, min, max ) );
1180 } else if ( symbolizerName.equals( "PolygonSymbolizer" ) ) {
1181 symbolizerList.add( createPolygonSymbolizer( symbolizerElement, min, max ) );
1182 } else if ( symbolizerName.equals( "TextSymbolizer" ) ) {
1183 symbolizerList.add( createTextSymbolizer( symbolizerElement, min, max ) );
1184 } else if ( symbolizerName.equals( "RasterSymbolizer" ) ) {
1185 symbolizerList.add( createRasterSymbolizer( symbolizerElement, min, max ) );
1186 }
1187 }
1188 }
1189
1190 Symbolizer[] symbolizers = symbolizerList.toArray( new Symbolizer[symbolizerList.size()] );
1191
1192 return new Rule( symbolizers, name, title, abstract_, legendGraphic, filter, isAnElseFilter, min, max );
1193 }
1194
1195 /**
1196 * Creates a <tt>PointSymbolizer</tt>-instance according to the contents of the DOM-subtree
1197 * starting at the given 'PointSymbolizer'-<tt>Element</tt>.
1198 * <p>
1199 *
1200 * @param element
1201 * the 'PointSymbolizer'-<tt>Element</tt>
1202 * @param min
1203 * scale-constraint to be used
1204 * @param max
1205 * scale-constraint to be used
1206 * @throws XMLParsingException
1207 * if a syntactic or semantic error in the DOM-subtree is encountered
1208 * @return the constructed <tt>PointSymbolizer</tt>-instance
1209 */
1210 private static PointSymbolizer createPointSymbolizer( Element element, double min, double max )
1211 throws XMLParsingException {
1212
1213 // optional: <Geometry>
1214 Geometry geometry = null;
1215 Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1216
1217 if ( geometryElement != null ) {
1218 geometry = createGeometry( geometryElement );
1219 }
1220
1221 // optional: <Graphic>
1222 Graphic graphic = null;
1223 Element graphicElement = XMLTools.getChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1224
1225 if ( graphicElement != null ) {
1226 graphic = createGraphic( graphicElement );
1227 }
1228
1229 PointSymbolizer ps = null;
1230 String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1231 if ( respClass == null ) {
1232 ps = new PointSymbolizer( graphic, geometry, min, max );
1233 } else {
1234 ps = new PointSymbolizer( graphic, geometry, respClass, min, max );
1235 }
1236
1237 return ps;
1238 }
1239
1240 /**
1241 * Creates a <tt>LineSymbolizer</tt>-instance according to the contents of the DOM-subtree
1242 * starting at the given 'LineSymbolizer'-<tt>Element</tt>.
1243 * <p>
1244 *
1245 * @param element
1246 * the 'LineSymbolizer'-<tt>Element</tt>
1247 * @param min
1248 * scale-constraint to be used
1249 * @param max
1250 * scale-constraint to be used
1251 * @throws XMLParsingException
1252 * if a syntactic or semantic error in the DOM-subtree is encountered
1253 * @return the constructed <tt>LineSymbolizer</tt>-instance
1254 */
1255 private static LineSymbolizer createLineSymbolizer( Element element, double min, double max )
1256 throws XMLParsingException {
1257
1258 // optional: <Geometry>
1259 Geometry geometry = null;
1260 Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1261
1262 if ( geometryElement != null ) {
1263 geometry = createGeometry( geometryElement );
1264 }
1265
1266 // optional: <Stroke>
1267 Stroke stroke = null;
1268 Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1269
1270 if ( strokeElement != null ) {
1271 stroke = createStroke( strokeElement );
1272 }
1273
1274 LineSymbolizer ls = null;
1275 String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1276 if ( respClass == null ) {
1277 ls = new LineSymbolizer( stroke, geometry, min, max );
1278 } else {
1279 ls = new LineSymbolizer( stroke, geometry, respClass, min, max );
1280 }
1281 return ls;
1282 }
1283
1284 /**
1285 * Creates a <tt>PolygonSymbolizer</tt>-instance according to the contents of the DOM-subtree
1286 * starting at the given 'PolygonSymbolizer'-<tt>Element</tt>.
1287 * <p>
1288 *
1289 * @param element
1290 * the 'PolygonSymbolizer'-<tt>Element</tt>
1291 * @param min
1292 * scale-constraint to be used
1293 * @param max
1294 * scale-constraint to be used
1295 * @throws XMLParsingException
1296 * if a syntactic or semantic error in the DOM-subtree is encountered
1297 * @return the constructed <tt>PolygonSymbolizer</tt>-instance
1298 */
1299 private static PolygonSymbolizer createPolygonSymbolizer( Element element, double min, double max )
1300 throws XMLParsingException {
1301 // optional: <Geometry>
1302 Geometry geometry = null;
1303 Element geometryElement = XMLTools.getChildElement( "Geometry", CommonNamespaces.SLDNS, element );
1304
1305 if ( geometryElement != null ) {
1306 geometry = createGeometry( geometryElement );
1307 }
1308
1309 // optional: <Fill>
1310 Fill fill = null;
1311 Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
1312
1313 if ( fillElement != null ) {
1314 fill = createFill( fillElement );
1315 }
1316
1317 // optional: <Stroke>
1318 Stroke stroke = null;
1319 Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1320
1321 if ( strokeElement != null ) {
1322 stroke = createStroke( strokeElement );
1323 }
1324
1325 PolygonSymbolizer ps = null;
1326 String respClass = XMLTools.getAttrValue( element, null, "responsibleClass", null );
1327 if ( respClass == null ) {
1328 ps = new PolygonSymbolizer( fill, stroke, geometry, min, max );
1329 } else {
1330 ps = new PolygonSymbolizer( fill, stroke, geometry, respClass, min, max );
1331 }
1332
1333 return ps;
1334 }
1335
1336 /**
1337 * Creates a <tt>Geometry</tt>-instance according to the contents of the DOM-subtree starting
1338 * at the given 'Geometry'-<tt>Element</tt>.
1339 * <p>
1340 * FIXME: Add support for 'Function'-Elements.
1341 * <p>
1342 *
1343 * @param element
1344 * the 'Geometry'-<tt>Element</tt>
1345 * @throws XMLParsingException
1346 * if a syntactic or semantic error in the DOM-subtree is encountered
1347 * @return the constructed <tt>Geometry</tt>-instance
1348 */
1349 private static Geometry createGeometry( Element element )
1350 throws XMLParsingException {
1351 Geometry geometry = null;
1352
1353 // required: <PropertyName>
1354 Element propertyNameElement = XMLTools.getRequiredChildElement( "PropertyName", ogcNS, element );
1355
1356 // optional: <Function>
1357 Element functionElement = XMLTools.getChildElement( "Function", ogcNS, propertyNameElement );
1358
1359 // just a property name exists
1360 if ( functionElement == null ) {
1361 Node node = XMLTools.getNode( propertyNameElement, "/text()", nsContext );
1362 PropertyPath pp = OGCDocument.parsePropertyPath( (Text) node );
1363 geometry = new Geometry( pp, null );
1364 } else {
1365 // FIXME:
1366 // the property probably contains a wfs:Function expression
1367 }
1368
1369 return geometry;
1370 }
1371
1372 /**
1373 * Creates a <tt>Fill</tt>-instance according to the contents of the DOM-subtree starting at
1374 * the given 'Fill'-<tt>Element</tt>.
1375 * <p>
1376 *
1377 * @param element
1378 * the 'Fill'-<tt>Element</tt>
1379 * @throws XMLParsingException
1380 * if a syntactic or semantic error in the DOM-subtree is encountered
1381 * @return the constructed <tt>Fill</tt>-instance
1382 */
1383 private static Fill createFill( Element element )
1384 throws XMLParsingException {
1385 // optional: <GraphicFill>
1386 GraphicFill graphicFill = null;
1387 Element graphicFillElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
1388
1389 if ( graphicFillElement != null ) {
1390 graphicFill = createGraphicFill( graphicFillElement );
1391 }
1392
1393 // optional: <CssParameter>s
1394 ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
1395 HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
1396
1397 for ( int i = 0; i < nl.getLength(); i++ ) {
1398 CssParameter cssParam = createCssParameter( nl.item( i ) );
1399 cssParams.put( cssParam.getName(), cssParam );
1400 }
1401
1402 return new Fill( cssParams, graphicFill );
1403 }
1404
1405 /**
1406 * Creates a <tt>LegendGraphic</tt>-instance according to the contents of the DOM-subtree
1407 * starting at the given 'LegendGraphic'-element.
1408 * <p>
1409 *
1410 * @param element
1411 * the 'LegendGraphic'-<tt>Element</tt>
1412 * @throws XMLParsingException
1413 * if a syntactic or semantic error in the DOM-subtree is encountered
1414 * @return the constructed <tt>Graphic</tt>-instance
1415 */
1416 private static LegendGraphic createLegendGraphic( Element element )
1417 throws XMLParsingException {
1418 // required: <Graphic>
1419 Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1420 Graphic graphic = createGraphic( graphicElement );
1421
1422 return new LegendGraphic( graphic );
1423 }
1424
1425 /**
1426 * Creates an <tt>ExternalGraphic</tt>-instance according to the contents of the DOM-subtree
1427 * starting at the given 'ExternalGraphic'-<tt>Element</tt>.
1428 * <p>
1429 *
1430 * @param element
1431 * the 'ExternalGraphic'-<tt>Element</tt>
1432 * @throws XMLParsingException
1433 * if a syntactic or semantic error in the DOM-subtree is encountered
1434 * @return the constructed <tt>ExternalGraphic</tt>-instance
1435 */
1436 private static ExternalGraphic createExternalGraphic( Element element )
1437 throws XMLParsingException {
1438 // required: <OnlineResource>
1439 Element onlineResourceElement = XMLTools.getRequiredChildElement( "OnlineResource", CommonNamespaces.SLDNS,
1440 element );
1441
1442 // required: href-Attribute (in <OnlineResource>)
1443 String href = XMLTools.getRequiredAttrValue( "href", xlnNS, onlineResourceElement );
1444 URL url = null;
1445 try {
1446 url = sldDoc.resolve( href );
1447 } catch ( MalformedURLException e ) {
1448 LOG.logDebug( e.getMessage(), e );
1449 throw new XMLParsingException( "Value ('" + href + "') of attribute 'href' of "
1450 + "element 'OnlineResoure' does not denote a valid URL" );
1451 }
1452
1453 // required: <Format> (in <OnlineResource>)
1454 String format = XMLTools.getRequiredStringValue( "Format", CommonNamespaces.SLDNS, element );
1455
1456 return new ExternalGraphic( format, url );
1457 }
1458
1459 /**
1460 * Creates a <tt>Mark</tt>-instance according to the contents of the DOM-subtree starting at
1461 * the given 'Mark'-<tt>Element</tt>.
1462 * <p>
1463 *
1464 * @param element
1465 * the 'Mark'-<tt>Element</tt>
1466 * @throws XMLParsingException
1467 * if a syntactic or semantic error in the DOM-subtree is encountered
1468 * @return the constructed <tt>Mark</tt>-instance
1469 */
1470 private static Mark createMark( Element element )
1471 throws XMLParsingException {
1472 Stroke stroke = null;
1473 Fill fill = null;
1474
1475 // optional: <WellKnownName>
1476 String wkn = XMLTools.getStringValue( "WellKnownName", CommonNamespaces.SLDNS, element, null );
1477
1478 // optional: <Stroke>
1479 Element strokeElement = XMLTools.getChildElement( "Stroke", CommonNamespaces.SLDNS, element );
1480
1481 if ( strokeElement != null ) {
1482 stroke = createStroke( strokeElement );
1483 }
1484
1485 // optional: <Fill>
1486 Element fillElement = XMLTools.getChildElement( "Fill", CommonNamespaces.SLDNS, element );
1487
1488 if ( fillElement != null ) {
1489 fill = createFill( fillElement );
1490 }
1491
1492 return new Mark( wkn, stroke, fill );
1493 }
1494
1495 /**
1496 * Creates a <tt>Stroke</tt>-instance according to the contents of the DOM-subtree starting
1497 * at the given 'Stroke'-<tt>Element</tt>.
1498 * <p>
1499 *
1500 * @param element
1501 * the 'Stroke'-<tt>Element</tt>
1502 * @throws XMLParsingException
1503 * if a syntactic or semantic error in the DOM-subtree is encountered
1504 * @return the constructed <tt>Stroke</tt>-instance
1505 */
1506 private static Stroke createStroke( Element element )
1507 throws XMLParsingException {
1508 GraphicFill gf = null;
1509 GraphicStroke gs = null;
1510
1511 // optional: <GraphicFill>
1512 Element gfElement = XMLTools.getChildElement( "GraphicFill", CommonNamespaces.SLDNS, element );
1513
1514 if ( gfElement != null ) {
1515 gf = createGraphicFill( gfElement );
1516 }
1517
1518 // optional: <GraphicStroke>
1519 Element gsElement = XMLTools.getChildElement( "GraphicStroke", CommonNamespaces.SLDNS, element );
1520
1521 if ( gsElement != null ) {
1522 gs = createGraphicStroke( gsElement );
1523 }
1524
1525 // optional: <CssParameter>s
1526 ElementList nl = XMLTools.getChildElements( "CssParameter", CommonNamespaces.SLDNS, element );
1527 HashMap<String, Object> cssParams = new HashMap<String, Object>( nl.getLength() );
1528
1529 for ( int i = 0; i < nl.getLength(); i++ ) {
1530 CssParameter cssParam = createCssParameter( nl.item( i ) );
1531 cssParams.put( cssParam.getName(), cssParam );
1532 }
1533
1534 return new Stroke( cssParams, gs, gf );
1535 }
1536
1537 /**
1538 * Creates a <tt>GraphicFill</tt>-instance according to the contents of the DOM-subtree
1539 * starting at the given 'GraphicFill'-<tt>Element</tt>.
1540 * <p>
1541 *
1542 * @param element
1543 * the 'GraphicFill'-<tt>Element</tt>
1544 * @throws XMLParsingException
1545 * if a syntactic or semantic error in the DOM-subtree is encountered
1546 * @return the constructed <tt>GraphicFill</tt>-instance
1547 */
1548 private static GraphicFill createGraphicFill( Element element )
1549 throws XMLParsingException {
1550 // required: <Graphic>
1551 Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1552 Graphic graphic = createGraphic( graphicElement );
1553
1554 return new GraphicFill( graphic );
1555 }
1556
1557 /**
1558 * Creates a <tt>GraphicStroke</tt>-instance according to the contents of the DOM-subtree
1559 * starting at the given 'GraphicStroke'-<tt>Element</tt>.
1560 * <p>
1561 *
1562 * @param element
1563 * the 'GraphicStroke'-<tt>Element</tt>
1564 * @throws XMLParsingException
1565 * if a syntactic or semantic error in the DOM-subtree is encountered
1566 * @return the constructed <tt>GraphicStroke</tt>-instance
1567 */
1568 private static GraphicStroke createGraphicStroke( Element element )
1569 throws XMLParsingException {
1570 // required: <Graphic>
1571 Element graphicElement = XMLTools.getRequiredChildElement( "Graphic", CommonNamespaces.SLDNS, element );
1572 Graphic graphic = createGraphic( graphicElement );
1573
1574 return new GraphicStroke( graphic );
1575 }
1576
1577 /**
1578 * Creates a <tt>Graphic</tt>-instance according to the contents of the DOM-subtree starting
1579 * at the given 'Graphic'-element.
1580 * <p>
1581 *
1582 * @param element
1583 * the 'Graphic'-<tt>Element</tt>
1584 * @throws XMLParsingException
1585 * if a syntactic or semantic error in the DOM-subtree is encountered
1586 * @return the constructed <tt>Graphic</tt>-instance
1587 */
1588 private static Graphic createGraphic( Element element )
1589 throws XMLParsingException {
1590
1591 // optional: <Opacity>
1592 ParameterValueType opacity = null;
1593 // optional: <Size>
1594 ParameterValueType size = null;
1595 // optional: <Rotation>
1596 ParameterValueType rotation = null;
1597
1598 // optional: <ExternalGraphic>s / <Mark>s
1599 NodeList nodelist = element.getChildNodes();
1600 ArrayList<Object> marksAndExtGraphicsList = new ArrayList<Object>();
1601
1602 for ( int i = 0; i < nodelist.getLength(); i++ ) {
1603 if ( nodelist.item( i ) instanceof Element ) {
1604 Element child = (Element) nodelist.item( i );
1605 String namespace = child.getNamespaceURI();
1606
1607 if ( !CommonNamespaces.SLDNS.toString().equals( namespace ) ) {
1608 continue;
1609 }
1610
1611 String childName = child.getLocalName();
1612
1613 if ( childName.equals( "ExternalGraphic" ) ) {
1614 marksAndExtGraphicsList.add( createExternalGraphic( child ) );
1615 } else if ( childName.equals( "Mark" ) ) {
1616 marksAndExtGraphicsList.add( createMark( child ) );
1617 } else if ( childName.equals( "Opacity" ) ) {
1618 opacity = createParameterValueType( child );
1619 } else if ( childName.equals( "Size" ) ) {
1620 size = createParameterValueType( child );
1621 } else if ( childName.equals( "Rotation" ) ) {
1622 rotation = createParameterValueType( child );
1623 }
1624 }
1625 }
1626
1627 Object[] marksAndExtGraphics = marksAndExtGraphicsList.toArray( new Object[marksAndExtGraphicsList.size()] );
1628
1629 return new Graphic( marksAndExtGraphics, opacity, size, rotation );
1630 }
1631
1632 /**
1633 * Creates a <tt>CssParameter</tt>-instance according to the contents of the DOM-subtree
1634 * starting at the given 'CssParameter'-<tt>Element</tt>.
1635 * <p>
1636 *
1637 * @param element
1638 * the 'CssParamter'-<tt>Element</tt>
1639 * @throws XMLParsingException
1640 * if a syntactic or semantic error in the DOM-subtree is encountered
1641 * @return the constructed <tt>CssParameter</tt>-instance
1642 */
1643 private static CssParameter createCssParameter( Element element )
1644 throws XMLParsingException {
1645 // required: name-Attribute
1646 String name = XMLTools.getRequiredAttrValue( "name", null, element );
1647 ParameterValueType pvt = createParameterValueType( element );
1648
1649 return ( new CssParameter( name, pvt ) );
1650 }
1651
1652 /**
1653 * <code>TresholdsBelongTo</code> enumerates values possibly belonging to
1654 * <code>ThreshholdsBelongToType</code>.
1655 *
1656 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1657 * @author last edited by: $Author: aschmitz $
1658 *
1659 * @version $Revision: 12138 $, $Date: 2008-06-04 10:34:15 +0200 (Mi, 04 Jun 2008) $
1660 */
1661 public enum ThresholdsBelongTo {
1662 /**
1663 * <code>"succeeding"</code>
1664 */
1665 SUCCEEDING,
1666 /**
1667 * <code>"preceding"</code>
1668 */
1669 PRECEDING
1670 }
1671
1672 /**
1673 * <code>Mode</code> is the ModeType from the Symbology Encoding Schema.
1674 *
1675 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1676 * @author last edited by: $Author: aschmitz $
1677 *
1678 * @version $Revision: 12138 $, $Date: 2008-06-04 10:34:15 +0200 (Mi, 04 Jun 2008) $
1679 */
1680 public enum Mode {
1681 /**
1682 * <code>"linear"</code>
1683 */
1684 LINEAR,
1685 /**
1686 * <code>"cosine"</code>
1687 */
1688 COSINE,
1689 /**
1690 * <code>"cubic"</code>
1691 */
1692 CUBIC
1693 }
1694
1695 /**
1696 * <code>Method</code> is the MethodType from the Symbology encoding Schema.
1697 *
1698 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
1699 * @author last edited by: $Author: aschmitz $
1700 *
1701 * @version $Revision: 12138 $, $Date: 2008-06-04 10:34:15 +0200 (Mi, 04 Jun 2008) $
1702 */
1703 public enum Method {
1704 /**
1705 * <code>"numeric"</code>
1706 */
1707 NUMERIC,
1708 /**
1709 * <code>"color"</code>
1710 */
1711 COLOR
1712 }
1713
1714 }