/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-2016, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.styling; import java.awt.Component; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.xml.parsers.ParserConfigurationException; import org.geotools.data.Base64; import org.geotools.factory.CommonFactoryFinder; import org.geotools.factory.GeoTools; import org.geotools.filter.ExpressionDOMParser; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; import org.geotools.util.GrowableInternationalString; import org.geotools.util.SimpleInternationalString; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.opengis.filter.FilterFactory2; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Function; import org.opengis.filter.expression.Literal; import org.opengis.filter.expression.PropertyName; import org.opengis.style.ContrastMethod; import org.opengis.util.InternationalString; import org.w3c.dom.CharacterData; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; /** * TODO: This really needs to be container ready * * @author jgarnett * * * * @source $URL$ http://svn.geotools.org/geotools/trunk/gt/modules/library/main/src/main/java/org/geotools /styling/SLDParser.java $ */ public class SLDParser { /** HISTOGRAM */ private static final String HISTOGRAM = "histogram"; /** EXPONENTIAL */ private static final String EXPONENTIAL = "exponential"; /** LOGARITHMIC */ private static final String LOGARITHMIC = "logarithmic"; /** NORMALIZE */ private static final String NORMALIZE = "normalize"; /** a list of available Contrast Methods */ final static List<String> CONTRAST_METHODS = Arrays.asList(NORMALIZE,LOGARITHMIC,EXPONENTIAL,HISTOGRAM); private static final java.util.logging.Logger LOGGER = org.geotools.util.logging.Logging .getLogger("org.geotools.styling"); private static final String channelSelectionString = "ChannelSelection"; private static final String graphicSt = "Graphic"; // to make pmd to shut up private static final String geomString = "Geometry"; // to make pmd to shut up private static final String fillSt = "Fill"; private static final String opacityString = "Opacity"; private static final String overlapBehaviorString = "OverlapBehavior"; private static final String colorMapString = "ColorMap"; private static final String colorMapOpacityString = "opacity"; private static final String colorMapColorString = "color"; private static final String contrastEnhancementString = "ContrastEnhancement"; private static final String shadedReliefString = "ShadedRelief"; private static final String imageOutlineString = "ImageOutline"; private static final String colorMapQuantityString = "quantity"; private static final String colorMapLabelString = "label"; private static final String strokeString = "Stroke"; private static final String uomString = "uom"; private static final String VendorOptionString = "VendorOption"; private static final String PerpendicularOffsetString = "PerpendicularOffset"; private static final Pattern WHITESPACES = Pattern.compile("\\s+", Pattern.MULTILINE); private static final Pattern LEADING_WHITESPACES = Pattern.compile("^\\s+"); private static final Pattern TRAILING_WHITESPACES = Pattern.compile("\\s+$"); private FilterFactory ff; protected InputSource source; private org.w3c.dom.Document dom; protected StyleFactory factory; /** useful for detecting relative onlineresources */ private URL sourceUrl; /** provides complete control for detecting relative onlineresources */ private ResourceLocator onlineResourceLocator; private EntityResolver entityResolver; /** * Create a Stylereader - use if you already have a dom to parse. * * @param factory The StyleFactory to use to build the style */ public SLDParser(StyleFactory factory) { this(factory, CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints())); } public SLDParser(StyleFactory factory, FilterFactory filterFactory) { this.factory = factory; this.ff = filterFactory; this.onlineResourceLocator = new DefaultResourceLocator(); } /** * Creates a new instance of SLDStyler * * @param factory The StyleFactory to use to read the file * @param filename The file to be read. * * @throws java.io.FileNotFoundException - if the file is missing */ public SLDParser(StyleFactory factory, String filename) throws java.io.FileNotFoundException { this(factory); File f = new File(filename); setInput(f); } /** * Creates a new SLDStyle object. * * @param factory The StyleFactory to use to read the file * @param f the File to be read * * @throws java.io.FileNotFoundException - if the file is missing */ public SLDParser(StyleFactory factory, File f) throws java.io.FileNotFoundException { this(factory); setInput(f); } /** * Creates a new SLDStyle object. * * @param factory The StyleFactory to use to read the file * @param url the URL to be read. * * @throws java.io.IOException - if something goes wrong reading the file */ public SLDParser(StyleFactory factory, java.net.URL url) throws java.io.IOException { this(factory); setInput(url); } /** * Creates a new SLDStyle object. * * @param factory The StyleFactory to use to read the file * @param s The inputstream to be read */ public SLDParser(StyleFactory factory, java.io.InputStream s) { this(factory); setInput(s); } /** * Creates a new SLDStyle object. * * @param factory The StyleFactory to use to read the file * @param r The inputstream to be read */ public SLDParser(StyleFactory factory, java.io.Reader r) { this(factory); setInput(r); } /** * set the file to read the SLD from * * @param filename the file to read the SLD from * * @throws java.io.FileNotFoundException if the file is missing */ public void setInput(String filename) throws java.io.FileNotFoundException { File f = new File(filename); source = new InputSource(new java.io.FileInputStream(f)); try { setSourceUrl(f.toURI().toURL()); } catch (MalformedURLException e) { LOGGER.warning("Can't build URL for file " + f.getAbsolutePath()); } } /** * Sets the file to use to read the SLD from * * @param f the file to use * * @throws java.io.FileNotFoundException if the file is missing */ public void setInput(File f) throws java.io.FileNotFoundException { source = new InputSource(new java.io.FileInputStream(f)); try { setSourceUrl(f.toURI().toURL()); } catch (MalformedURLException e) { LOGGER.warning("Can't build URL for file " + f.getAbsolutePath()); } } /** * sets an URL to read the SLD from * * @param url the url to read the SLD from * * @throws java.io.IOException If anything goes wrong opening the url */ public void setInput(java.net.URL url) throws java.io.IOException { source = new InputSource(url.openStream()); setSourceUrl(url); } /** * Sets the input stream to read the SLD from * * @param in the inputstream used to read the SLD from */ public void setInput(java.io.InputStream in) { source = new InputSource(in); } /** * Sets the input stream to read the SLD from * * @param in the inputstream used to read the SLD from */ public void setInput(java.io.Reader in) { source = new InputSource(in); } /** * Sets the resource loader implementation for parsing online resources. */ public void setOnLineResourceLocator(ResourceLocator onlineResourceLocator) { this.onlineResourceLocator = onlineResourceLocator; } /** * Sets the EntityResolver implementation that will be used by DocumentBuilder to resolve XML external entities. * * @param entityResolver */ public void setEntityResolver(EntityResolver entityResolver) { this.entityResolver = entityResolver; } /** * Internal setter for source url. */ void setSourceUrl(URL sourceUrl) { this.sourceUrl = sourceUrl; if (onlineResourceLocator instanceof DefaultResourceLocator) { ((DefaultResourceLocator) onlineResourceLocator).setSourceUrl(sourceUrl); } } protected javax.xml.parsers.DocumentBuilder newDocumentBuilder(boolean namespaceAware) throws ParserConfigurationException { javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory .newInstance(); dbf.setNamespaceAware(namespaceAware); javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder(); if (entityResolver != null) { db.setEntityResolver(entityResolver); } return db; } /** * Read the xml inputsource provided and create a Style object for each user style found * * @return Style[] the styles constructed. * * @throws RuntimeException if a parsing error occurs */ public Style[] readXML() { try { dom = newDocumentBuilder(true).parse(source); } catch (javax.xml.parsers.ParserConfigurationException pce) { throw new RuntimeException(pce); } catch (org.xml.sax.SAXException se) { throw new RuntimeException(se); } catch (java.io.IOException ie) { throw new RuntimeException(ie); } return readDOM(dom); } /** * Read styles from the dom that was previously parsed. */ public Style[] readDOM() { if (dom == null) { throw new NullPointerException("dom is null"); } return readDOM(dom); } /** * Read the DOM provided and create a Style object for each user style found * * @param document a dom containing the SLD * * @return Style[] the styles constructed. */ public Style[] readDOM(org.w3c.dom.Document document) { this.dom = document; // for our next trick do something with the dom. NodeList nodes = findElements(document, "UserStyle"); final int length = nodes.getLength(); if (nodes == null) return new Style[0]; Style[] styles = new Style[length]; for (int i = 0; i < length; i++) { styles[i] = parseStyle(nodes.item(i)); } return styles; } /** * @param document * @param name */ private NodeList findElements(final org.w3c.dom.Document document, final String name) { NodeList nodes = document.getElementsByTagNameNS("*", name); if (nodes.getLength() == 0) { nodes = document.getElementsByTagName(name); } return nodes; } private NodeList findElements(final org.w3c.dom.Element element, final String name) { NodeList nodes = element.getElementsByTagNameNS("*", name); if (nodes.getLength() == 0) { nodes = element.getElementsByTagName(name); } return nodes; } public StyledLayerDescriptor parseSLD() { try { dom = newDocumentBuilder(true).parse(source); // for our next trick do something with the dom. // NodeList nodes = findElements(dom, "StyledLayerDescriptor"); StyledLayerDescriptor sld = parseDescriptor(dom.getDocumentElement());// should only be // one per file return sld; } catch (javax.xml.parsers.ParserConfigurationException pce) { throw new RuntimeException(pce); } catch (org.xml.sax.SAXException se) { throw new RuntimeException(se); } catch (java.io.IOException ie) { throw new RuntimeException(ie); } } public StyledLayerDescriptor parseDescriptor(Node root) { StyledLayerDescriptor sld = factory.createStyledLayerDescriptor(); // StyledLayer layer = null; // LineSymbolizer symbol = factory.createLineSymbolizer(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("Name")) { sld.setName(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("Title")) { sld.setTitle(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("Abstract")) { sld.setAbstract(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("NamedLayer")) { NamedLayer layer = parseNamedLayer(child); sld.addStyledLayer(layer); } else if (childName.equalsIgnoreCase("UserLayer")) { StyledLayer layer = parseUserLayer(child); sld.addStyledLayer(layer); } } return sld; } /** * Returns the first child node value, or null if there is no child * * @param child * @return */ String getFirstChildValue(Node child) { if (child.getFirstChild() != null) return child.getFirstChild().getNodeValue(); else return null; } private static String getAttribute(Node node, String attrName) { NamedNodeMap attributes = node.getAttributes(); Node attribute = attributes.getNamedItem(attrName); return attribute == null ? null : attribute.getNodeValue(); } private StyledLayer parseUserLayer(Node root) { UserLayer layer = new UserLayerImpl(); // LineSymbolizer symbol = factory.createLineSymbolizer(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("InlineFeature")) { parseInlineFeature(child, layer); } else if (childName.equalsIgnoreCase("UserStyle")) { Style user = parseStyle(child); layer.addUserStyle(user); } else if (childName.equalsIgnoreCase("Name")) { String layerName = getFirstChildValue(child); layer.setName(layerName); if (LOGGER.isLoggable(Level.INFO)) LOGGER.info("layer name: " + layer.getName()); } else if (childName.equalsIgnoreCase("RemoteOWS")) { RemoteOWS remoteOws = parseRemoteOWS(child); layer.setRemoteOWS(remoteOws); } else if (childName.equalsIgnoreCase("LayerFeatureConstraints")) { layer.setLayerFeatureConstraints(parseLayerFeatureConstraints(child)); } } return layer; } private FeatureTypeConstraint[] parseLayerFeatureConstraints(Node root) { List<FeatureTypeConstraint> featureTypeConstraints = new ArrayList<FeatureTypeConstraint>(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName.equalsIgnoreCase("FeatureTypeConstraint")) { final FeatureTypeConstraint ftc = parseFeatureTypeConstraint(child); if (ftc != null) featureTypeConstraints.add(ftc); } } return featureTypeConstraints .toArray(new FeatureTypeConstraint[featureTypeConstraints.size()]); } protected FeatureTypeConstraint parseFeatureTypeConstraint(Node root) { FeatureTypeConstraint ftc = new FeatureTypeConstraintImpl(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName.equalsIgnoreCase("FeatureTypeName")) { ftc.setFeatureTypeName(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("Filter")) { ftc.setFilter(parseFilter(child)); } } ftc.setExtents(new Extent[0]); if (ftc.getFeatureTypeName() == null) return null; else return ftc; } private static Icon parseIcon(String content) throws IOException { byte[] bytes = Base64.decode(content); BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes)); if (image == null) { throw new IOException("invalid image content"); } return new ImageIcon(image); } protected RemoteOWS parseRemoteOWS(Node root) { RemoteOWS ows = new RemoteOWSImpl(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName.equalsIgnoreCase("Service")) { ows.setService(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("OnlineResource")) { ows.setOnlineResource(parseOnlineResource(child)); } } return ows; } /** * * @param child * @param layer */ private void parseInlineFeature(Node root, UserLayer layer) { try { SLDInlineFeatureParser inparser = new SLDInlineFeatureParser(root); layer.setInlineFeatureDatastore(inparser.dataStore); layer.setInlineFeatureType(inparser.featureType); } catch (Exception e) { throw (IllegalArgumentException) new IllegalArgumentException().initCause(e); } } /** * Parses a NamedLayer. * <p> * The NamedLayer schema is: * * <pre> * <code> * <xsd:element name="NamedLayer"> * <xsd:annotation> * <xsd:documentation> A NamedLayer is a layer of data that has a name advertised by a WMS. </xsd:documentation> * </xsd:annotation> * <xsd:complexType> * <xsd:sequence> * <xsd:element ref="sld:Name"/> * <xsd:element ref="sld:LayerFeatureConstraints" minOccurs="0"/> * <xsd:choice minOccurs="0" maxOccurs="unbounded"> * <xsd:element ref="sld:NamedStyle"/> * <xsd:element ref="sld:UserStyle"/> * </xsd:choice> * </xsd:sequence> * </xsd:complexType> * </xsd:element> * </code> * </pre> * * </p> * * @param root */ private NamedLayer parseNamedLayer(Node root) { NamedLayer layer = new NamedLayerImpl(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("Name")) { layer.setName(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("NamedStyle")) { NamedStyle style = parseNamedStyle(child); layer.addStyle(style); } else if (childName.equalsIgnoreCase("UserStyle")) { Style user = parseStyle(child); layer.addStyle(user); } else if (childName.equalsIgnoreCase("LayerFeatureConstraints")) { layer.setLayerFeatureConstraints(parseLayerFeatureConstraints(child)); } } return layer; } /** * Parses a NamedStyle from node. * <p> * A NamedStyle is used to refer to a style that has a name in a WMS, and is defined as: * * <pre> * <code> * <xsd:element name="NamedStyle"> * <xsd:annotation> * <xsd:documentation> A NamedStyle is used to refer to a style that has a name in a WMS. </xsd:documentation> * </xsd:annotation> * <xsd:complexType> * <xsd:sequence> * <xsd:element ref="sld:Name"/> * </xsd:sequence> * </xsd:complexType> * </xsd:element> * </code> * </pre> * * </p> * * @param n */ public NamedStyle parseNamedStyle(Node n) { if (dom == null) { try { dom = newDocumentBuilder(false).newDocument(); } catch (javax.xml.parsers.ParserConfigurationException pce) { throw new RuntimeException(pce); } } NamedStyle style = factory.createNamedStyle(); NodeList children = n.getChildNodes(); final int length = children.getLength(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("" + children.getLength() + " children to process"); } for (int j = 0; j < length; j++) { Node child = children.item(j); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE) || (child.getFirstChild() == null)) { continue; } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing " + child.getLocalName()); } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("Name")) { style.setName(getFirstChildValue(child)); } } return style; } /** * build a style for the Node provided * * @param n the node which contains the style to be parsed. * * @return the Style constructed. * * @throws RuntimeException if an error occurs setting up the parser */ public Style parseStyle(Node n) { if (dom == null) { try { dom = newDocumentBuilder(false).newDocument(); } catch (javax.xml.parsers.ParserConfigurationException pce) { throw new RuntimeException(pce); } } Style style = factory.createStyle(); NodeList children = n.getChildNodes(); final int length = children.getLength(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("" + children.getLength() + " children to process"); } for (int j = 0; j < length; j++) { Node child = children.item(j); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE) || (child.getFirstChild() == null)) { continue; } // System.out.println("The child is: " + child.getNodeName() + " or // " + child.getLocalName() + " prefix is " +child.getPrefix()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing " + child.getLocalName()); } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } String firstChildValue = getFirstChildValue(child); if (childName.equalsIgnoreCase("Name")) { style.setName(firstChildValue); } else if (childName.equalsIgnoreCase("Title")) { style.getDescription().setTitle(parseInternationalString(child)); } else if (childName.equalsIgnoreCase("Abstract")) { style.getDescription().setAbstract(parseInternationalString(child)); } else if (childName.equalsIgnoreCase("IsDefault")) { if ("1".equals(firstChildValue)) { style.setDefault(true); } else { style.setDefault(Boolean.valueOf(firstChildValue).booleanValue()); } } else if (childName.equalsIgnoreCase("FeatureTypeStyle")) { style.addFeatureTypeStyle(parseFeatureTypeStyle(child)); } } return style; } /** Internal parse method - made protected for unit testing */ protected FeatureTypeStyle parseFeatureTypeStyle(Node style) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("Parsing featuretype style " + style.getLocalName()); } FeatureTypeStyle ft = factory.createFeatureTypeStyle(); ArrayList<Rule> rules = new ArrayList<Rule>(); ArrayList<String> sti = new ArrayList<String>(); NodeList children = style.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing " + child.getLocalName()); } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("Name")) { ft.setName(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("Title")) { ft.getDescription().setTitle(parseInternationalString(child)); } else if (childName.equalsIgnoreCase("Abstract")) { ft.getDescription().setAbstract(parseInternationalString(child)); } else if (childName.equalsIgnoreCase("FeatureTypeName")) { ft.setFeatureTypeName(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("SemanticTypeIdentifier")) { sti.add(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("Rule")) { rules.add(parseRule(child)); } else if (childName.equalsIgnoreCase("Transformation")) { ExpressionDOMParser parser = new ExpressionDOMParser( CommonFactoryFinder.getFilterFactory2(null)); Expression tx = parser.expression(getFirstNonTextChild(child)); ft.setTransformation(tx); } else if (childName.equalsIgnoreCase(VendorOptionString)) { parseVendorOption(ft.getOptions(), child); } } if (sti.size() > 0) { ft.setSemanticTypeIdentifiers(sti.toArray(new String[0])); } ft.setRules(rules.toArray(new Rule[0])); return ft; } private Node getFirstNonTextChild(Node node) { NodeList children = node.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } return child; } return null; } /** Internal parse method - made protected for unit testing */ protected Rule parseRule(Node ruleNode) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("Parsing rule " + ruleNode.getLocalName()); } Rule rule = factory.createRule(); List<Symbolizer> symbolizers = new ArrayList<Symbolizer>(); NodeList children = ruleNode.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.indexOf(':') != -1) { // the DOM parser wasnt properly set to handle namespaces... childName = childName.substring(childName.indexOf(':') + 1); } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing " + child.getLocalName()); } if (childName.equalsIgnoreCase("Name")) { rule.setName(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("Title")) { rule.getDescription().setTitle(parseInternationalString(child)); } else if (childName.equalsIgnoreCase("Abstract")) { rule.getDescription().setAbstract(parseInternationalString(child)); } else if (childName.equalsIgnoreCase("MinScaleDenominator")) { rule.setMinScaleDenominator(Double.parseDouble(getFirstChildValue(child))); } else if (childName.equalsIgnoreCase("MaxScaleDenominator")) { rule.setMaxScaleDenominator(Double.parseDouble(getFirstChildValue(child))); } else if (childName.equalsIgnoreCase("Filter")) { Filter filter = parseFilter(child); rule.setFilter(filter); } else if (childName.equalsIgnoreCase("ElseFilter")) { rule.setElseFilter(true); } else if (childName.equalsIgnoreCase("LegendGraphic")) { findElements(((Element) child), graphicSt); NodeList g = findElements(((Element) child), graphicSt); List<Graphic> legends = new ArrayList<Graphic>(); final int l = g.getLength(); for (int k = 0; k < l; k++) { legends.add(parseGraphic(g.item(k))); } rule.setLegendGraphic(legends.toArray(new Graphic[0])); } else if (childName.equalsIgnoreCase("LineSymbolizer")) { symbolizers.add(parseLineSymbolizer(child)); } else if (childName.equalsIgnoreCase("PolygonSymbolizer")) { symbolizers.add(parsePolygonSymbolizer(child)); } else if (childName.equalsIgnoreCase("PointSymbolizer")) { symbolizers.add(parsePointSymbolizer(child)); } else if (childName.equalsIgnoreCase("TextSymbolizer")) { symbolizers.add(parseTextSymbolizer(child)); } else if (childName.equalsIgnoreCase("RasterSymbolizer")) { symbolizers.add(parseRasterSymbolizer(child)); } } rule.setSymbolizers(symbolizers.toArray(new Symbolizer[0])); return rule; } /** * Parse a node with mixed content containing internationalized elements in the form: <Localized lang="locale">text</Localized> * * @param root * @return */ private InternationalString parseInternationalString(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsingInternationalString " + root); } NodeList children = root.getChildNodes(); final int length = children.getLength(); StringBuilder text = new StringBuilder(); Map<String, String> translations = new HashMap<String, String>(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null)) { continue; } else if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) { // append text as is String value = child.getNodeValue(); if (value == null) continue; text.append(value.trim()); } else if (child.getNodeType() == Node.ELEMENT_NODE) { // parse value elements if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("about to parse " + child.getLocalName()); } Element element = (Element) child; if (element.getTagName().equalsIgnoreCase("localized")) { String lang = element.getAttribute("lang"); String translation = getFirstChildValue(element); translations.put(lang, translation); } } else continue; } if (translations.size() > 0) { GrowableInternationalString intString = new GrowableInternationalString( text.toString()) { @Override public String toString() { return super.toString(null); } }; for (String lang : translations.keySet()) { intString.add("", "_" + lang, translations.get(lang)); } return intString; } else { String simpleText = getFirstChildValue(root); return new SimpleInternationalString(simpleText == null ? "" : simpleText); } } /** Internal parse method - made protected for unit testing */ protected Filter parseFilter(Node child) { // this sounds stark raving mad, but this is actually how the dom parser // works... // instead of passing in the parent element, pass in the first child and // its // siblings will also be parsed Node firstChild = child.getFirstChild(); while (firstChild != null && firstChild.getNodeType() != Node.ELEMENT_NODE) { // advance to the first actual element (rather than whitespace) firstChild = firstChild.getNextSibling(); } Filter filter = org.geotools.filter.FilterDOMParser.parseFilter(firstChild); return filter; } /** * parses the SLD for a linesymbolizer * * @param root a w2c Dom Node * * @return the linesymbolizer */ protected LineSymbolizer parseLineSymbolizer(Node root) { LineSymbolizer symbol = factory.createLineSymbolizer(); NamedNodeMap namedNodeMap = root.getAttributes(); Node uomNode = namedNodeMap.getNamedItem(uomString); if (uomNode != null) { UomOgcMapping uomMapping = UomOgcMapping.get(uomNode.getNodeValue()); symbol.setUnitOfMeasure(uomMapping.getUnit()); } NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(geomString)) { symbol.setGeometry(parseGeometry(child)); } else if (childName.equalsIgnoreCase(strokeString)) { symbol.setStroke(parseStroke(child)); } else if (childName.equalsIgnoreCase(VendorOptionString)) { parseVendorOption(symbol.getOptions(), child); } else if (childName.equalsIgnoreCase(PerpendicularOffsetString)) { final Expression offsetValue = parseCssParameter(child); symbol.setPerpendicularOffset(offsetValue); } } return symbol; } /** * parses the SLD for a polygonsymbolizer * * @param root w3c dom node * * @return the polygon symbolizer */ protected PolygonSymbolizer parsePolygonSymbolizer(Node root) { PolygonSymbolizer symbol = factory.createPolygonSymbolizer(); symbol.setFill((Fill) null); symbol.setStroke((Stroke) null); NamedNodeMap namedNodeMap = root.getAttributes(); Node uomNode = namedNodeMap.getNamedItem(uomString); if (uomNode != null) { UomOgcMapping uomMapping = UomOgcMapping.get(uomNode.getNodeValue()); symbol.setUnitOfMeasure(uomMapping.getUnit()); } NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(geomString)) { symbol.setGeometry(parseGeometry(child)); } else if (childName.equalsIgnoreCase(strokeString)) { symbol.setStroke(parseStroke(child)); } else if (childName.equalsIgnoreCase(fillSt)) { symbol.setFill(parseFill(child)); } else if (childName.equalsIgnoreCase(VendorOptionString)) { parseVendorOption(symbol.getOptions(), child); } } return symbol; } /** * parses the SLD for a text symbolizer * * @param root w3c dom node * * @return the TextSymbolizer */ protected TextSymbolizer parseTextSymbolizer(Node root) { TextSymbolizer symbol = factory.createTextSymbolizer(); symbol.setFill(null); NamedNodeMap namedNodeMap = root.getAttributes(); Node uomNode = namedNodeMap.getNamedItem(uomString); if (uomNode != null) { UomOgcMapping uomMapping = UomOgcMapping.get(uomNode.getNodeValue()); symbol.setUnitOfMeasure(uomMapping.getUnit()); } List<Font> fonts = new ArrayList<Font>(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(geomString)) { symbol.setGeometry(parseGeometry(child)); } else if (childName.equalsIgnoreCase(fillSt)) { symbol.setFill(parseFill(child)); } else if (childName.equalsIgnoreCase("Label")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("parsing label " + child.getNodeValue()); // the label parser should collapse whitespaces to one, so // we call parseCssParameter with trimWhiteSpace=false symbol.setLabel(parseCssParameter(child, false)); if (symbol.getLabel() == null) { if (LOGGER.isLoggable(Level.WARNING)) LOGGER.warning( "parsing TextSymbolizer node - couldnt find anything in the Label element!"); } } if (childName.equalsIgnoreCase("Font")) { fonts.add(parseFont(child)); } else if (childName.equalsIgnoreCase("LabelPlacement")) { symbol.setLabelPlacement(parseLabelPlacement(child)); } else if (childName.equalsIgnoreCase("Halo")) { symbol.setHalo(parseHalo(child)); } else if (childName.equalsIgnoreCase("Graphic")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("Parsing non-standard Graphic in TextSymbolizer"); if (symbol instanceof TextSymbolizer2) { ((TextSymbolizer2) symbol).setGraphic(parseGraphic(child)); } } else if (childName.equalsIgnoreCase("Snippet")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("Parsing non-standard Abstract in TextSymbolizer"); if (symbol instanceof TextSymbolizer2) ((TextSymbolizer2) symbol).setSnippet(parseCssParameter(child, false)); } else if (childName.equalsIgnoreCase("FeatureDescription")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("Parsing non-standard Description in TextSymbolizer"); if (symbol instanceof TextSymbolizer2) ((TextSymbolizer2) symbol) .setFeatureDescription(parseCssParameter(child, false)); } else if (childName.equalsIgnoreCase("OtherText")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("Parsing non-standard OtherText in TextSymbolizer"); if (symbol instanceof TextSymbolizer2) ((TextSymbolizer2) symbol).setOtherText(parseOtherText(child)); } else if (childName.equalsIgnoreCase("priority")) { symbol.setPriority(parseCssParameter(child)); } else if (childName.equalsIgnoreCase(VendorOptionString)) { parseVendorOption(symbol.getOptions(), child); } } symbol.fonts().addAll(fonts); return symbol; } protected OtherText parseOtherText(Node root) { // TODO: add methods to the factory to create OtherText instances OtherText ot = new OtherTextImpl(); final Node targetAttribute = root.getAttributes().getNamedItem("target"); if (targetAttribute == null) throw new IllegalArgumentException( "OtherLocation does not have the " + "required 'target' attribute"); String target = targetAttribute.getNodeValue(); Expression text = parseCssParameter(root, true); ot.setTarget(target); ot.setText(text); return ot; } /** * adds the key/value pair from the node ("<VendorOption name="...">...</VendorOption>") * * @param symbol * @param child */ private void parseVendorOption(Map<String, String> options, Node child) { String key = child.getAttributes().getNamedItem("name").getNodeValue(); String value = getFirstChildValue(child); options.put(key, value); } /** * parses the SLD for a text symbolizer * * @param root w3c dom node * * @return the TextSymbolizer */ protected RasterSymbolizer parseRasterSymbolizer(Node root) { final RasterSymbolizer symbol = factory.getDefaultRasterSymbolizer(); NamedNodeMap namedNodeMap = root.getAttributes(); Node uomNode = namedNodeMap.getNamedItem(uomString); if (uomNode != null) { UomOgcMapping uomMapping = UomOgcMapping.get(uomNode.getNodeValue()); symbol.setUnitOfMeasure(uomMapping.getUnit()); } NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(geomString)) { symbol.setGeometry(parseGeometry(child)); } if (childName.equalsIgnoreCase(opacityString)) { try { final String opacityString = getFirstChildValue(child); Expression opacity = parseParameterValueExpression(child, false); symbol.setOpacity(opacity); } catch (Throwable e) { if (LOGGER.isLoggable(Level.WARNING)) LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); } } else if (childName.equalsIgnoreCase(channelSelectionString)) { symbol.setChannelSelection(parseChannelSelection(child)); } else if (childName.equalsIgnoreCase(overlapBehaviorString)) { try { final String overlapString = child.getFirstChild().getLocalName(); symbol.setOverlap(ff.literal(overlapString)); } catch (Throwable e) { if (LOGGER.isLoggable(Level.WARNING)) LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); } } else if (childName.equalsIgnoreCase(colorMapString)) { symbol.setColorMap(parseColorMap(child)); } else if (childName.equalsIgnoreCase(contrastEnhancementString)) { symbol.setContrastEnhancement(parseContrastEnhancement(child)); } else if (childName.equalsIgnoreCase(shadedReliefString)) { symbol.setShadedRelief(parseShadedRelief(child)); } else if (childName.equalsIgnoreCase(imageOutlineString)) { symbol.setImageOutline(parseLineSymbolizer(child)); } } return symbol; } /** * Many elements in an SLD extends ParameterValueType (allowing for the definition of Expressions) - this method will try and produce an * expression for the provided node. * <p> * As an example: * <ul> * <li>"sld:Opacity" is defined as a parameter value type:<br> * <sld:Opacity>0.75<\sld:Opacity> * <li>"sld:Label" is defined as a "mixed" parameter value type:<br> * <sld:Label>Hello <sld:PropertyName>name<\sld:PropertyName><\sld:Label> * </ul> * From the SLD 1.0 spec: "ParameterValueType" uses WFS-Filter expressions to give values for SLD graphic parameters. A "mixed" element-content * model is used with textual substitution for values. */ Expression parseParameterValueExpression(Node root, boolean mixedText) { ExpressionDOMParser parser = new ExpressionDOMParser((FilterFactory2) ff); Expression expr = parser.expression(root); // try the provided node first if (expr != null) return expr; NodeList children = root.getChildNodes(); // if there is only one CharacterData node - we can make a literal out of it if (children.getLength() == 1 && root.getFirstChild() instanceof CharacterData) { Node textNode = root.getFirstChild(); String text = textNode.getNodeValue(); return ff.literal(text.trim()); } List<Expression> expressionList = new ArrayList<Expression>(); for (int index = 0; index < children.getLength(); index++) { Node child = children.item(index); if (child instanceof CharacterData) { if (mixedText) { String text = child.getNodeValue(); Expression childExpr = ff.literal(text); expressionList.add(childExpr); } } else { Expression childExpr = parser.expression(child); if (childExpr != null) { expressionList.add(childExpr); } } } if (expressionList.isEmpty()) { return Expression.NIL; } else if (expressionList.size() == 1) { return expressionList.get(0); } else if (expressionList.size() == 2) { Expression[] expressionArray = expressionList.toArray(new Expression[0]); return ff.function("strConcat", expressionArray); } else { Expression[] expressionArray = expressionList.toArray(new Expression[0]); return ff.function("Concatenate", expressionArray); } } /** Internal parse method - made protected for unit testing */ protected ColorMapEntry parseColorMapEntry(Node root) { ColorMapEntry symbol = factory.createColorMapEntry(); NamedNodeMap atts = root.getAttributes(); if (atts.getNamedItem(colorMapLabelString) != null) { symbol.setLabel(atts.getNamedItem(colorMapLabelString).getNodeValue()); } if (atts.getNamedItem(colorMapColorString) != null) { symbol.setColor(ff.literal(atts.getNamedItem(colorMapColorString).getNodeValue())); } if (atts.getNamedItem(colorMapOpacityString) != null) { symbol.setOpacity(ff.literal(atts.getNamedItem(colorMapOpacityString).getNodeValue())); } if (atts.getNamedItem(colorMapQuantityString) != null) { symbol.setQuantity( ff.literal(atts.getNamedItem(colorMapQuantityString).getNodeValue())); } return symbol; } /** Internal parse method - made protected for unit testing */ protected ColorMap parseColorMap(Node root) { ColorMap symbol = factory.createColorMap(); if (root.hasAttributes()) { // parsing type attribute final NamedNodeMap atts = root.getAttributes(); Node typeAtt = atts.getNamedItem("type"); if (typeAtt != null) { final String type = typeAtt.getNodeValue(); if ("ramp".equalsIgnoreCase(type)) { symbol.setType(ColorMap.TYPE_RAMP); } else if ("intervals".equalsIgnoreCase(type)) { symbol.setType(ColorMap.TYPE_INTERVALS); } else if ("values".equalsIgnoreCase(type)) { symbol.setType(ColorMap.TYPE_VALUES); } else if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, "ColorMapType", type)); } // parsing extended colors typeAtt = atts.getNamedItem("extended"); if (typeAtt != null) { final String type = typeAtt.getNodeValue(); if ("true".equalsIgnoreCase(type)) { symbol.setExtendedColors(true); } else if ("false".equalsIgnoreCase(type)) { symbol.setExtendedColors(false); } else if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, "Extended", type)); } } NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("ColorMapEntry")) { symbol.addColorMapEntry(parseColorMapEntry(child)); } } return symbol; } /** Internal parse method - made protected for unit testing */ protected SelectedChannelType parseSelectedChannel(Node root) { SelectedChannelType symbol = new SelectedChannelTypeImpl(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } else if (childName.equalsIgnoreCase("SourceChannelName")) { if (child.getFirstChild() != null && child.getFirstChild().getNodeType() == Node.TEXT_NODE) symbol.setChannelName(getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("ContrastEnhancement")) { symbol.setContrastEnhancement(parseContrastEnhancement(child)); /* * try { if (child.getFirstChild() != null && child.getFirstChild().getNodeType() == Node.TEXT_NODE) * symbol.setContrastEnhancement((Expression) ExpressionBuilder .parse(child.getFirstChild().getNodeValue())); } catch (Exception e) { * // TODO: handle exception } */ } } return symbol; } /** Internal parse method - made protected for unit testing */ protected ChannelSelection parseChannelSelection(Node root) { List<SelectedChannelType> channels = new ArrayList<SelectedChannelType>(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } else if (childName.equalsIgnoreCase("GrayChannel")) { channels.add(parseSelectedChannel(child)); } else if (childName.equalsIgnoreCase("RedChannel")) { channels.add(parseSelectedChannel(child)); } else if (childName.equalsIgnoreCase("GreenChannel")) { channels.add(parseSelectedChannel(child)); } else if (childName.equalsIgnoreCase("BlueChannel")) { channels.add(parseSelectedChannel(child)); } } ChannelSelection dap = factory .createChannelSelection(channels.toArray(new SelectedChannelType[channels.size()])); return dap; } /** Internal parse method - made protected for unit testing */ protected ContrastEnhancement parseContrastEnhancement(Node root) { ContrastEnhancement symbol = new ContrastEnhancementImpl(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (CONTRAST_METHODS.contains(childName.toLowerCase())) { ContrastMethodStrategy parseContrastMethod = parseContrastMethod(childName,child); symbol.setMethod(parseContrastMethod.getMethod()); symbol.setOptions(parseContrastMethod.getOptions()); } else if (childName.equalsIgnoreCase("GammaValue")) { try { final String gammaString = getFirstChildValue(child); symbol.setGammaValue(ff.literal(Double.parseDouble(gammaString))); } catch (Exception e) { if (LOGGER.isLoggable(Level.WARNING)) LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); } } } return symbol; } /** * @return */ private ContrastMethodStrategy parseContrastMethod(String name, Node root) { ContrastMethod met = ContrastMethod.NONE; ContrastMethodStrategy ret = null; if(NORMALIZE.equalsIgnoreCase(name)) { met = ContrastMethod.NORMALIZE; ret= new NormalizeContrastMethodStrategy(); }else if (HISTOGRAM.equalsIgnoreCase(name)) { met = ContrastMethod.HISTOGRAM; ret = new HistogramContrastMethodStrategy(); }else if (LOGARITHMIC.equalsIgnoreCase(name)) { met = ContrastMethod.LOGARITHMIC; ret = new LogarithmicContrastMethodStrategy(); }else if (EXPONENTIAL.equalsIgnoreCase(name)) { met = ContrastMethod.EXPONENTIAL; ret = new ExponentialContrastMethodStrategy(); } ret.setMethod(met); //now extract any VendorOptions NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if ("VendorOption".equalsIgnoreCase(childName)) { String key = getAttribute(child, "name"); if (key == null) continue; Expression value = parseCssParameter(child); if (value == null) continue; ret.addOption(key, value); } } return ret; } /** Internal parse method - made protected for unit testing */ protected ShadedRelief parseShadedRelief(Node root) { ShadedRelief symbol = new ShadedReliefImpl(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if ("BrightnessOnly".equalsIgnoreCase(childName)) { symbol.setBrightnessOnly(Boolean.getBoolean(getFirstChildValue(child))); } else if ("ReliefFactor".equalsIgnoreCase(childName)) { try { final String reliefString = getFirstChildValue(child); Expression relief = ExpressionDOMParser.parseExpression(child); symbol.setReliefFactor(relief); } catch (Exception e) { if (LOGGER.isLoggable(Level.WARNING)) LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); } } } return symbol; } /** * parses the SLD for a point symbolizer * * @param root a w3c dom node * * @return the pointsymbolizer */ protected PointSymbolizer parsePointSymbolizer(Node root) { PointSymbolizer symbol = factory.getDefaultPointSymbolizer(); // symbol.setGraphic(null); NamedNodeMap namedNodeMap = root.getAttributes(); Node uomNode = namedNodeMap.getNamedItem(uomString); if (uomNode != null) { UomOgcMapping uomMapping = UomOgcMapping.get(uomNode.getNodeValue()); symbol.setUnitOfMeasure(uomMapping.getUnit()); } NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(geomString)) { symbol.setGeometry(parseGeometry(child)); } else if (childName.equalsIgnoreCase(graphicSt)) { symbol.setGraphic(parseGraphic(child)); } else if (childName.equalsIgnoreCase(VendorOptionString)) { parseVendorOption(symbol.getOptions(), child); } } return symbol; } /** Internal parse method - made protected for unit testing */ protected Graphic parseGraphic(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing graphic " + root); } Graphic graphic = factory.getDefaultGraphic(); NodeList children = root.getChildNodes(); final int length = children.getLength(); boolean firstGraphic = true; for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("ExternalGraphic")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("parsing extgraphic " + child); if (firstGraphic) { graphic.graphicalSymbols().clear(); firstGraphic = false; } graphic.graphicalSymbols().add(parseExternalGraphic(child)); } else if (childName.equalsIgnoreCase("Mark")) { if (firstGraphic) { graphic.graphicalSymbols().clear(); firstGraphic = false; } graphic.graphicalSymbols().add(parseMark(child)); } else if (childName.equalsIgnoreCase(opacityString)) { graphic.setOpacity(parseCssParameter(child)); } else if (childName.equalsIgnoreCase("size")) { graphic.setSize(parseCssParameter(child)); } else if (childName.equalsIgnoreCase("displacement")) { graphic.setDisplacement(parseDisplacement(child)); } else if (childName.equalsIgnoreCase("anchorPoint")) { graphic.setAnchorPoint(parseAnchorPoint(child)); } else if (childName.equalsIgnoreCase("rotation")) { graphic.setRotation(parseCssParameter(child)); } } return graphic; } /** Internal parse method - made protected for unit testing */ protected String parseGeometryName(Node root) { Expression result = parseGeometry(root); if (result instanceof PropertyName) { return ((PropertyName) result).getPropertyName(); } return null; } /** Internal parse method - made protected for unit testing */ protected Expression parseGeometry(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("parsing GeometryExpression"); } return parseCssParameter(root); } /** Internal parse method - made protected for unit testing */ protected Mark parseMark(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing mark"); } Mark mark = factory.createMark(); mark.setFill(null); mark.setStroke(null); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(strokeString)) { mark.setStroke(parseStroke(child)); } else if (childName.equalsIgnoreCase(fillSt)) { mark.setFill(parseFill(child)); } else if (childName.equalsIgnoreCase("WellKnownName")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("setting mark to " + getFirstChildValue(child)); mark.setWellKnownName(parseCssParameter(child)); } } return mark; } /** Internal parse method - made protected for unit testing */ protected ExternalGraphic parseExternalGraphic(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing external graphic "); } String format = ""; String uri = ""; String content = null; Map<String, Object> paramList = new HashMap<String, Object>(); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("InlineContent")) { String contentEncoding = getAttribute(child, "encoding"); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("inline content with encoding " + contentEncoding); } if ("base64".equals(contentEncoding)) { content = getFirstChildValue(child); } else { content = ""; if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning( "could not process <" + contentEncoding + "> content encoding"); } } } else if (childName.equalsIgnoreCase("OnLineResource")) { uri = parseOnlineResource(child); } if (childName.equalsIgnoreCase("format")) { LOGGER.finest("format child is " + child); LOGGER.finest("seting ExtGraph format " + getFirstChildValue(child)); format = (getFirstChildValue(child)); } else if (childName.equalsIgnoreCase("customProperty")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("custom child is " + child); String propName = child.getAttributes().getNamedItem("name").getNodeValue(); if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("seting custom property " + propName + " to " + getFirstChildValue(child)); Expression value = parseCssParameter(child); paramList.put(propName, value); } } ExternalGraphic extgraph; if (content != null) { Icon icon = null; if (content.length() > 0) { try { icon = parseIcon(content); } catch (IOException e) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "could not parse graphic inline content: " + content, e); } } } if (icon == null) { LOGGER.warning("returning empty icon"); icon = EmptyIcon.INSTANCE; } extgraph = factory.createExternalGraphic(icon, format); } else { URL url = onlineResourceLocator.locateResource(uri); if (url == null) { extgraph = factory.createExternalGraphic(uri, format); } else { extgraph = factory.createExternalGraphic(url, format); } } extgraph.setCustomProperties(paramList); return extgraph; } /** Internal parse method - made protected for unit testing */ protected String parseOnlineResource(Node root) { Element param = (Element) root; org.w3c.dom.NamedNodeMap map = param.getAttributes(); final int length = map.getLength(); LOGGER.finest("attributes " + map.toString()); for (int k = 0; k < length; k++) { String res = map.item(k).getNodeValue(); String name = map.item(k).getNodeName(); // if(name == null){ // name = map.item(k).getNodeName(); // } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing attribute " + name + "=" + res); } // TODO: process the name space properly if (name.equalsIgnoreCase("xlink:href") || name.equalsIgnoreCase("href")) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("seting ExtGraph uri " + res); return res; } } return null; } /** Internal parse method - made protected for unit testing */ protected Stroke parseStroke(Node root) { Stroke stroke = factory.getDefaultStroke(); NodeList list = findElements(((Element) root), "GraphicFill"); int length = list.getLength(); if (length > 0) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("stroke: found a graphic fill " + list.item(0)); NodeList kids = list.item(0).getChildNodes(); for (int i = 0; i < kids.getLength(); i++) { Node child = kids.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(graphicSt)) { Graphic g = parseGraphic(child); if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("setting stroke graphicfill with " + g); stroke.setGraphicFill(g); } } } list = findElements(((Element) root), "GraphicStroke"); length = list.getLength(); if (length > 0) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("stroke: found a graphic stroke " + list.item(0)); NodeList kids = list.item(0).getChildNodes(); for (int i = 0; i < kids.getLength(); i++) { Node child = kids.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(graphicSt)) { Graphic g = parseGraphic(child); if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("setting stroke graphicStroke with " + g); stroke.setGraphicStroke(g); } } } list = findElements(((Element) root), "CssParameter"); length = list.getLength(); for (int i = 0; i < length; i++) { Node child = list.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("now I am processing " + child); } Element param = (Element) child; org.w3c.dom.NamedNodeMap map = param.getAttributes(); final int mapLength = map.getLength(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("attributes " + map.toString()); } for (int k = 0; k < mapLength; k++) { String res = map.item(k).getNodeValue(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing attribute " + res); } // process the css entry // if (res.equalsIgnoreCase(strokeString)) { Expression color = parseCssParameter(child, true); stroke.setColor(color); } else if (res.equalsIgnoreCase("width") || res.equalsIgnoreCase("stroke-width")) { Expression width = parseCssParameter(child, false); stroke.setWidth(width); } else if (res.equalsIgnoreCase(opacityString) || res.equalsIgnoreCase("stroke-opacity")) { Expression opacity = parseCssParameter(child, false); stroke.setOpacity(opacity); } else if (res.equalsIgnoreCase("linecap") || res.equalsIgnoreCase("stroke-linecap")) { // since these are system-dependent just pass them through and hope. stroke.setLineCap(parseCssParameter(child)); } else if (res.equalsIgnoreCase("linejoin") || res.equalsIgnoreCase("stroke-linejoin")) { // since these are system-dependent just pass them through // and hope. stroke.setLineJoin(parseCssParameter(child)); } else if (res.equalsIgnoreCase("dasharray") || res.equalsIgnoreCase("stroke-dasharray")) { stroke.setDashArray(parseDashArray(child)); } else if (res.equalsIgnoreCase("dashoffset") || res.equalsIgnoreCase("stroke-dashoffset")) { stroke.setDashOffset(parseCssParameter(child)); } } } return stroke; } private List<Expression> parseDashArray(Node root) { NodeList children = root.getChildNodes(); List<Expression> expressions = new ArrayList<>(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (child == null) continue; switch (child.getNodeType()) { case Node.TEXT_NODE: handleDashArrayText(child.getNodeValue(), expressions); break; case Node.ELEMENT_NODE: handleDashArrayNode(child, expressions); break; case Node.CDATA_SECTION_NODE: handleDashArrayText(child.getNodeValue(), expressions); break; } } return expressions; } private void handleDashArrayText(String text, List<Expression> expressions) { if (text == null || text.isEmpty()) { return; } for (String textPart : text.split("\\s+")) { if(!textPart.isEmpty()) { expressions.add(ff.literal(Float.valueOf(textPart))); } } } private void handleDashArrayNode(Node child, List<Expression> expressions) { Expression expression = ExpressionDOMParser.parseExpression(child); if (expression instanceof Literal) { handleDashArrayLiteral((Literal) expression, expressions); } else { expressions.add(expression); } } private void handleDashArrayLiteral(Literal literal, List<Expression> expressions) { Object value = literal.getValue(); if (value instanceof String) { handleDashArrayText((String) value, expressions); } else { expressions.add(literal); } } /** Internal parse method - made protected for unit testing */ protected Fill parseFill(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing fill "); } Fill fill = factory.getDefaultFill(); NodeList list = findElements(((Element) root), "GraphicFill"); int length = list.getLength(); if (length > 0) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("fill found a graphic fill " + list.item(0)); NodeList kids = list.item(0).getChildNodes(); for (int i = 0; i < kids.getLength(); i++) { Node child = kids.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(graphicSt)) { Graphic g = parseGraphic(child); if (LOGGER.isLoggable(Level.FINEST)) LOGGER.finest("setting fill graphic with " + g); fill.setGraphicFill(g); } } } list = findElements(((Element) root), "CssParameter"); length = list.getLength(); for (int i = 0; i < length; i++) { Node child = list.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } Element param = (Element) child; org.w3c.dom.NamedNodeMap map = param.getAttributes(); final int mapLength = map.getLength(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("now I am processing " + child); } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("attributes " + map.toString()); } for (int k = 0; k < mapLength; k++) { String res = map.item(k).getNodeValue(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("processing attribute " + res); } if (res.equalsIgnoreCase(fillSt)) { fill.setColor(parseCssParameter(child)); } else if (res.equalsIgnoreCase(opacityString) || res.equalsIgnoreCase("fill-opacity")) { fill.setOpacity(parseCssParameter(child)); } } } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("fill graphic " + fill.getGraphicFill()); } return fill; } /** * Concatenates the given expressions (through the strConcat FunctionFilter expression) * * @param left * @param right * @return */ private Expression manageMixed(Expression left, Expression right) { if (left == null) return right; if (right == null) return left; Function mixed = ff.function("strConcat", new Expression[] { left, right }); return mixed; } /** * Parses a css parameter. Default implementation trims whitespaces from text nodes. * * @param root node to parse * @return */ private Expression parseCssParameter(Node root) { return parseCssParameter(root, true); } /** * Parses a css parameter. You can choose if the parser must trim whitespace from text nodes or not. * * @param root node to parse * @param trimWhiteSpace true to trim whitespace from text nodes. If false, whitespaces will be collapsed into one * @return */ private Expression parseCssParameter(Node root, boolean trimWhiteSpace) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsingCssParam " + root); } NodeList children = root.getChildNodes(); final int length = children.getLength(); List<Expression> expressions = new ArrayList<Expression>(); List<Boolean> cdatas = new ArrayList<Boolean>(); for (int i = 0; i < length; i++) { Node child = children.item(i); // Added mixed="true" management through concatenation of text and // expression nodes if ((child == null)) { continue; } else if (child.getNodeType() == Node.TEXT_NODE) { String value = child.getNodeValue(); if (value == null) continue; if (trimWhiteSpace) { value = value.trim(); } else { // by spec the inner spaces should collapsed into one, leading and trailing // space should be eliminated too // http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/ (4.3.6 whiteSpace) // remove inside spaces value = WHITESPACES.matcher(value).replaceAll(" "); // we can't deal with leading and trailing whitespaces now // as the parser will return each line of whitespace as a separate element // we have to do that as post processing } if (value != null && value.length() != 0) { Literal literal = ff.literal(value); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("Built new literal " + literal); } // add the text node as a literal expressions.add(literal); cdatas.add(false); } } else if (child.getNodeType() == Node.ELEMENT_NODE) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("about to parse " + child.getLocalName()); } // add the element node as an expression expressions.add(org.geotools.filter.ExpressionDOMParser.parseExpression(child)); cdatas.add(false); } else if (child.getNodeType() == Node.CDATA_SECTION_NODE) { String value = child.getNodeValue(); if (value != null && value.length() != 0) { // we build a literal straight, to preserve even cdata sections // that have only spaces (as opposed to try and parse it as a literal // using the expression dom parser) Literal literal = ff.literal(value); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("Built new literal " + literal); } // add the text node as a literal expressions.add(literal); cdatas.add(true); } } else continue; } if (expressions.size() == 0 && LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("no children in CssParam"); } if (!trimWhiteSpace) { // remove all leading white spaces, which means, find all // string literals, remove the white space ones, eventually // remove the leading white space form the first non white space one while (expressions.size() > 0) { Expression ex = expressions.get(0); // if it's not a string literal we're done if (!(ex instanceof Literal)) break; Literal literal = (Literal) ex; if (!(literal.getValue() instanceof String)) break; // ok, string literal. String s = (String) literal.getValue(); if (!cdatas.get(0)) { if ("".equals(s.trim())) { // If it's whitespace, we have to remove it and continue expressions.remove(0); cdatas.remove(0); } else { // if it's not only whitespace, remove anyways the eventual whitespace // at its beginning, and then exit, leading whitespace removal is done if (s.startsWith(" ")) { s = LEADING_WHITESPACES.matcher(s).replaceAll(""); expressions.set(0, ff.literal(s)); } break; } } else { break; } } // remove also all trailing white spaces the same way while (expressions.size() > 0) { final int idx = expressions.size() - 1; Expression ex = expressions.get(idx); // if it's not a string literal we're done if (!(ex instanceof Literal)) break; Literal literal = (Literal) ex; if (!(literal.getValue() instanceof String)) break; // ok, string literal. String s = (String) literal.getValue(); if (!cdatas.get(idx)) { if ("".equals(s.trim())) { // If it's whitespace, we have to remove it and continue expressions.remove(idx); cdatas.remove(idx); } else { // if it's not only whitespace, remove anyways the eventual whitespace // at its end, and then exit, trailing whitespace removal is done if (s.endsWith(" ")) { s = TRAILING_WHITESPACES.matcher(s).replaceAll(""); expressions.set(idx, ff.literal(s)); } break; } } else { break; } } } // now combine all expressions into one Expression ret = null; for (Expression expression : expressions) { ret = manageMixed(ret, expression); } //If the expression list is empty, we have an empty tag, and should return an empty string. if (ret == null) { return ff.literal(""); } return ret; } /** * Internal method to parse a Font Node; protected to allow for unit testing */ protected Font parseFont(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing font"); } Font font = factory.getDefaultFont(); boolean firstFontFamily = true; NodeList list = findElements(((Element) root), "CssParameter"); int length = list.getLength(); for (int i = 0; i < length; i++) { Node child = list.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } Element param = (Element) child; org.w3c.dom.NamedNodeMap map = param.getAttributes(); final int mapLength = map.getLength(); for (int k = 0; k < mapLength; k++) { String res = map.item(k).getNodeValue(); if (res.equalsIgnoreCase("font-family")) { if(firstFontFamily) { // wipe out the default font font.getFamily().clear(); firstFontFamily = false; } // use add instead of set as we might have multiple fonts here font.getFamily().add(parseCssParameter(child)); } else if (res.equalsIgnoreCase("font-style")) { font.setFontStyle(parseCssParameter(child)); } else if (res.equalsIgnoreCase("font-size")) { font.setFontSize(parseCssParameter(child)); } else if (res.equalsIgnoreCase("font-weight")) { font.setFontWeight(parseCssParameter(child)); } } } return font; } /** Internal parse method - made protected for unit testing */ protected LabelPlacement parseLabelPlacement(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing labelPlacement"); } LabelPlacement ret = null; NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("PointPlacement")) { ret = parsePointPlacement(child); } else if (childName.equalsIgnoreCase("LinePlacement")) { ret = parseLinePlacement(child); } } return ret; } /** Internal parse method - made protected for unit testing */ protected PointPlacement parsePointPlacement(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing pointPlacement"); } Expression rotation = ff.literal(0.0); AnchorPoint ap = null; Displacement dp = null; NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("AnchorPoint")) { ap = (parseAnchorPoint(child)); } else if (childName.equalsIgnoreCase("Displacement")) { dp = (parseDisplacement(child)); } else if (childName.equalsIgnoreCase("Rotation")) { rotation = (parseCssParameter(child)); } } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("setting anchorPoint " + ap); LOGGER.fine("setting displacement " + dp); } PointPlacement dpp = factory.createPointPlacement(ap, dp, rotation); return dpp; } /** Internal parse method - made protected for unit testing */ protected LinePlacement parseLinePlacement(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing linePlacement"); } Expression offset = ff.literal(0.0); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("PerpendicularOffset")) { offset = parseCssParameter(child); } } LinePlacement dlp = factory.createLinePlacement(offset); return dlp; } /** * Internal method to parse an AnchorPoint node; protected visibility for testing. * * @param root * @return */ protected AnchorPoint parseAnchorPoint(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing anchorPoint"); } Expression x = null; Expression y = null; NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("AnchorPointX")) { x = (parseCssParameter(child)); } else if (childName.equalsIgnoreCase("AnchorPointY")) { y = (parseCssParameter(child)); } } AnchorPoint dap = factory.createAnchorPoint(x, y); return dap; } /** Internal parse method - made protected for unit testing */ protected Displacement parseDisplacement(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing displacment"); } Expression x = null; Expression y = null; NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase("DisplacementX")) { x = (parseCssParameter(child)); } if (childName.equalsIgnoreCase("DisplacementY")) { y = (parseCssParameter(child)); } } Displacement dd = factory.createDisplacement(x, y); return dd; } /** Internal parse method - made protected for unit testing */ protected Halo parseHalo(Node root) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("parsing halo"); } Halo halo = factory.createHalo(factory.createFill(ff.literal("#FFFFFF")), ff.literal(1.0)); NodeList children = root.getChildNodes(); final int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) { continue; } String childName = child.getLocalName(); if (childName == null) { childName = child.getNodeName(); } if (childName.equalsIgnoreCase(fillSt)) { halo.setFill(parseFill(child)); } else if (childName.equalsIgnoreCase("Radius")) { halo.setRadius(parseCssParameter(child)); } } return halo; } private static class EmptyIcon implements Icon { public static final EmptyIcon INSTANCE = new EmptyIcon(); @Override public void paintIcon(Component c, Graphics g, int x, int y) { } @Override public int getIconWidth() { return 1; } @Override public int getIconHeight() { return 1; } } }