/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2006-2008, 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.visitor; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Stack; import org.geotools.factory.CommonFactoryFinder; import org.geotools.filter.visitor.DuplicatingFilterVisitor; import org.geotools.styling.AnchorPoint; import org.geotools.styling.ChannelSelection; import org.geotools.styling.ColorMap; import org.geotools.styling.ColorMapEntry; import org.geotools.styling.ContrastEnhancement; import org.geotools.styling.DescriptionImpl; import org.geotools.styling.Displacement; import org.geotools.styling.Extent; import org.geotools.styling.ExternalGraphic; import org.geotools.styling.FeatureTypeConstraint; import org.geotools.styling.FeatureTypeStyle; import org.geotools.styling.FeatureTypeStyleImpl; import org.geotools.styling.Fill; import org.geotools.styling.Font; import org.geotools.styling.Graphic; import org.geotools.styling.Halo; import org.geotools.styling.ImageOutline; import org.geotools.styling.LabelPlacement; import org.geotools.styling.LinePlacement; import org.geotools.styling.LineSymbolizer; import org.geotools.styling.Mark; import org.geotools.styling.NamedLayer; import org.geotools.styling.OtherText; import org.geotools.styling.OtherTextImpl; import org.geotools.styling.OverlapBehavior; import org.geotools.styling.PointPlacement; import org.geotools.styling.PointSymbolizer; import org.geotools.styling.PolygonSymbolizer; import org.geotools.styling.RasterSymbolizer; import org.geotools.styling.Rule; import org.geotools.styling.SelectedChannelType; import org.geotools.styling.ShadedRelief; import org.geotools.styling.Stroke; import org.geotools.styling.Style; import org.geotools.styling.StyleFactory; import org.geotools.styling.StyleFactoryImpl; import org.geotools.styling.StyleVisitor; import org.geotools.styling.StyledLayer; import org.geotools.styling.StyledLayerDescriptor; import org.geotools.styling.Symbol; import org.geotools.styling.Symbolizer; import org.geotools.styling.TextSymbolizer; import org.geotools.styling.TextSymbolizer2; import org.geotools.styling.UserLayer; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.filter.expression.Expression; import org.opengis.style.Description; /** * Creates a deep copy of a Style, this class is *NOT THREAD SAFE*. *

* This class makes use of an internal stack to story the copied result, * retrieve with a call to getCopy() after visiting:


 * DuplicatingStyleVisitor copyStyle = new DuplicatingStyleVisitor();
 * rule.accepts( copyStyle );
 * Rule rule = (Rule) copyStyle.getCopy();
 * 
*

* This class is often used as a base for an anoymous subclass where * a style transformation is needed (such as removing PointSymbolizers * or changing the scale - see RescaleStyleVisitor for an example). *

* @author Jesse Eichar * * * * @source $URL$ */ public class DuplicatingStyleVisitor implements StyleVisitor { protected final StyleFactory sf; protected final FilterFactory2 ff; protected boolean STRICT; /** * We are using aggregation here to contain our DuplicatingFilterVisitor. */ protected final DuplicatingFilterVisitor copyFilter; /** * This is our internal stack; used to maintain state as we copy sub elements. */ protected Stack pages=new Stack(); public DuplicatingStyleVisitor() { this( CommonFactoryFinder.getStyleFactory( null ) ); } public DuplicatingStyleVisitor(StyleFactory styleFactory) { this( styleFactory, CommonFactoryFinder.getFilterFactory2( null )); } public DuplicatingStyleVisitor(StyleFactory styleFactory, FilterFactory2 filterFactory ) { this.copyFilter = new DuplicatingFilterVisitor( filterFactory ); this.sf=styleFactory; this.ff=filterFactory; this.STRICT = false; } /** * True if we should enforce equality after a copy. * @param strict */ public void setStrict(boolean strict) { STRICT = strict; } public Object getCopy() { return pages.peek(); } public void visit(StyledLayerDescriptor sld) { StyledLayerDescriptor copy = null; StyledLayer[] layers = sld.getStyledLayers(); StyledLayer[] layersCopy = new StyledLayer[layers.length]; final int length=layers.length; for (int i = 0; i < length; i++) { if (layers[i] instanceof UserLayer) { ((UserLayer) layers[i]).accept(this); layersCopy[i] = (UserLayer) pages.pop(); } else if (layers[i] instanceof NamedLayer) { ((NamedLayer) layers[i]).accept(this); layersCopy[i] = (NamedLayer) pages.pop(); } } copy = sf.createStyledLayerDescriptor(); copy.setAbstract(sld.getAbstract()); copy.setName(sld.getName()); copy.setTitle(sld.getTitle()); copy.setStyledLayers(layersCopy); if( STRICT && !copy.equals( sld )){ throw new IllegalStateException("Was unable to duplicate provided SLD:"+sld ); } pages.push(copy); } public void visit(NamedLayer layer) { NamedLayer copy = null; Style[] style = layer.getStyles(); Style[] styleCopy = new Style[style.length]; int length=style.length; for (int i = 0; i < length; i++) { if (style[i] != null) { style[i].accept(this); styleCopy[i] = (Style) pages.pop(); } } FeatureTypeConstraint[] lfc = layer.getLayerFeatureConstraints(); FeatureTypeConstraint[] lfcCopy = new FeatureTypeConstraint[lfc.length]; length=lfc.length; for (int i = 0; i < length; i++) { if (lfc[i] != null) { lfc[i].accept(this); lfcCopy[i] = (FeatureTypeConstraint) pages.pop(); } } copy = sf.createNamedLayer(); copy.setName(layer.getName()); length=styleCopy.length; for (int i = 0; i < length; i++) { copy.addStyle(styleCopy[i]); } copy.setLayerFeatureConstraints(lfcCopy); pages.push(copy); } public void visit(UserLayer layer) { UserLayer copy = null; Style[] style = layer.getUserStyles(); int length=style.length; Style[] styleCopy = new Style[length]; for (int i = 0; i < length; i++) { if (style[i] != null) { style[i].accept(this); styleCopy[i] = (Style) pages.pop(); } } FeatureTypeConstraint[] lfc = layer.getLayerFeatureConstraints(); FeatureTypeConstraint[] lfcCopy = new FeatureTypeConstraint[lfc.length]; length=lfc.length; for (int i = 0; i < length; i++) { if (lfc[i] != null) { lfc[i].accept(this); lfcCopy[i] = (FeatureTypeConstraint) pages.pop(); } } copy = sf.createUserLayer(); copy.setName(layer.getName()); copy.setUserStyles(styleCopy); copy.setLayerFeatureConstraints(lfcCopy); if( STRICT && !copy.equals( layer )){ throw new IllegalStateException("Was unable to duplicate provided UserLayer:"+layer ); } pages.push(copy); } public void visit(Style style) { Style copy = null; FeatureTypeStyle[] fts = style.getFeatureTypeStyles(); final int length=fts.length; FeatureTypeStyle[] ftsCopy = new FeatureTypeStyle[length]; for (int i = 0; i < length; i++) { if (fts[i] != null) { fts[i].accept(this); ftsCopy[i] = (FeatureTypeStyle) pages.pop(); } } copy = sf.createStyle(); copy.setAbstract(style.getAbstract()); copy.setName(style.getName()); copy.setTitle(style.getTitle()); copy.setFeatureTypeStyles(ftsCopy); if( STRICT && !copy.equals( style )){ throw new IllegalStateException("Was unable to duplicate provided Style:"+style ); } pages.push(copy); } public void visit(Rule rule) { Rule copy = null; Filter filterCopy = null; if (rule.getFilter() != null) { Filter filter = rule.getFilter(); filterCopy = copy( filter ); } Symbolizer[] symsCopy = rule.getSymbolizers(); for (int i = 0; i < symsCopy.length; i++) { symsCopy[i] = copy(symsCopy[i]); } Graphic[] legendCopy = rule.getLegendGraphic(); for (int i = 0; i < legendCopy.length; i++) { legendCopy[i] = copy(legendCopy[i]); } Description descCopy = rule.getDescription(); descCopy = copy(descCopy); copy = sf.createRule(); copy.setSymbolizers(symsCopy); copy.setDescription(descCopy); copy.setLegendGraphic(legendCopy); copy.setName(rule.getName()); copy.setFilter(filterCopy); copy.setElseFilter(rule.isElseFilter()); copy.setMaxScaleDenominator(rule.getMaxScaleDenominator()); copy.setMinScaleDenominator(rule.getMinScaleDenominator()); if( STRICT && !copy.equals( rule )){ throw new IllegalStateException("Was unable to duplicate provided Rule:"+rule ); } pages.push(copy); } public void visit(FeatureTypeStyle fts) { FeatureTypeStyle copy = new FeatureTypeStyleImpl( (FeatureTypeStyleImpl)fts); // copy = new StyleFactoryImpl().createFeatureTypeStyle( // fts.getRules(), // fts.getSemanticTypeIdentifiers(), // fts.featureInstanceIDs(), // fts.getFeatureTypeName(), // fts.getDescription(), // fts.getName()); Rule[] rules = fts.getRules(); int length=rules.length; Rule[] rulesCopy = new Rule[length]; for (int i = 0; i < length; i++) { if (rules[i] != null) { rules[i].accept(this); rulesCopy[i] = (Rule) pages.pop(); } } // // copy = sf.createFeatureTypeStyle(); // copy.setName(fts.getName()); // copy.setTitle(fts.getTitle()); // copy.setAbstract(fts.getAbstract()); // copy.setFeatureTypeName(fts.getFeatureTypeName()); copy.setRules(rulesCopy); // copy.setSemanticTypeIdentifiers((String[]) fts.getSemanticTypeIdentifiers().clone()); if( STRICT && !copy.equals( fts )){ throw new IllegalStateException("Was unable to duplicate provided FeatureTypeStyle:"+fts ); } pages.push(copy); } /** * Null safe expression copy. *

* This method will perform a null check, and save you some lines of code:


     * copy.setBackgroundColor( copyExpr( fill.getColor()) );
     * 
* @param sion * @return copy of expression or null if expression was null */ protected Expression copy( Expression expression ){ if( expression == null ) return null; return (Expression) expression.accept( copyFilter, ff ); } /** * Null safe copy of filter. */ protected Filter copy( Filter filter ){ if( filter == null ) return null; return (Filter) filter.accept( copyFilter, ff ); } /** * Null safe graphic copy * @param graphic * @return copy of graphic or null if not provided */ protected Graphic copy( Graphic graphic ){ if( graphic == null ) return null; graphic.accept(this); return (Graphic) pages.pop(); } /** * Null safe fill copy * @param graphic * @return copy of graphic or null if not provided */ protected Fill copy( Fill fill ){ if( fill == null ) return null; fill.accept(this); return (Fill) pages.pop(); } /** * Null safe copy of float array. * @param array * @return copy of array or null if not provided */ protected float[] copy(float[] array) { if( array == null ) return null; float copy[] = new float[ array.length]; System.arraycopy( array, 0, copy, 0, array.length ); return copy; } /** * Null safe map copy, used for external graphic custom properties. * @param customProperties * @return copy of map */ @SuppressWarnings("unchecked") protected Map copy(Map customProperties) { if( customProperties == null ) return null; return new HashMap( customProperties ); } /** * Null safe copy of stroke. * @param stroke * @return copy of stroke if provided */ protected Stroke copy( Stroke stroke ){ if( stroke == null ) return null; stroke.accept(this); return (Stroke) pages.pop(); } /** * Null safe copy of shaded relief. * @param shaded * @return copy of shaded or null if not provided */ protected ShadedRelief copy(ShadedRelief shaded) { if( shaded == null ) return null; Expression reliefFactor = copy( shaded.getReliefFactor() ); ShadedRelief copy = sf.createShadedRelief( reliefFactor ); copy.setBrightnessOnly( shaded.isBrightnessOnly() ); return copy; } /** * Null safe copy of description * @param shaded * @return copy of shaded or null if not provided */ protected Description copy(Description desc) { if( desc == null ) return null; DescriptionImpl copy = new DescriptionImpl(desc.getTitle(), desc.getAbstract()); return copy; } protected ExternalGraphic copy( ExternalGraphic externalGraphic){ if( externalGraphic == null ) return null; externalGraphic.accept(this); return (ExternalGraphic) pages.pop(); } protected Mark copy( Mark mark){ if( mark == null ) return null; mark.accept(this); return (Mark) pages.pop(); } protected ColorMapEntry copy(ColorMapEntry entry) { if( entry == null ) return null; entry.accept( this ); return (ColorMapEntry) pages.pop(); } protected Symbolizer copy(Symbolizer symbolizer) { if( symbolizer == null ) return null; symbolizer.accept(this); return (Symbolizer) pages.pop(); } protected OverlapBehavior copy(OverlapBehavior ob) { if( ob == null ) return null; ob.accept(this); return (OverlapBehavior) pages.pop(); } protected ContrastEnhancement copy(ContrastEnhancement contrast) { if( contrast == null ) return null; ContrastEnhancement copy = sf.createContrastEnhancement(); copy.setGammaValue( copy( contrast.getGammaValue())); copy.setMethod(contrast.getMethod()); if(contrast.getType() != null) { copy.setType(contrast.getType()); } return copy; } protected ColorMap copy(ColorMap colorMap) { if( colorMap == null ) return null; colorMap.accept(this); return (ColorMap) pages.pop(); } protected SelectedChannelType[] copy( SelectedChannelType[] channels){ if( channels == null ) return null; SelectedChannelType[] copy = new SelectedChannelType[ channels.length ]; for( int i=0; i< channels.length ; i++){ copy[i] = copy( channels[i] ); } return copy; } protected SelectedChannelType copy(SelectedChannelType selectedChannelType) { if( selectedChannelType == null ) return null; ContrastEnhancement enhancement = copy( selectedChannelType.getContrastEnhancement() ); String name = selectedChannelType.getChannelName(); SelectedChannelType copy = sf.createSelectedChannelType( name, enhancement); return copy; } protected ChannelSelection copy(ChannelSelection channelSelection) { if( channelSelection == null ) return null; SelectedChannelType[] channels = copy( channelSelection.getSelectedChannels() ); ChannelSelection copy = sf.createChannelSelection( channels); copy.setGrayChannel( copy( channelSelection.getGrayChannel() )); copy.setRGBChannels( copy( channelSelection.getRGBChannels() )); return copy; } /** * Null safe copy of font array. *

* Right now style visitor does not let us visit fonts! * @param fonts * @return copy of provided fonts */ protected Font[] copy(Font[] fonts) { if( fonts == null ) return null; Font copy[] = new Font[ fonts.length ]; for( int i=0; i