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 }