001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wmps/operation/PrintMap.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53115 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     
043     ---------------------------------------------------------------------------*/
044    package org.deegree.ogcwebservices.wmps.operation;
045    
046    import java.awt.Color;
047    import java.io.Serializable;
048    import java.io.UnsupportedEncodingException;
049    import java.net.URLDecoder;
050    import java.sql.Timestamp;
051    import java.util.ArrayList;
052    import java.util.Arrays;
053    import java.util.HashMap;
054    import java.util.List;
055    import java.util.Map;
056    import java.util.StringTokenizer;
057    
058    import org.deegree.framework.log.ILogger;
059    import org.deegree.framework.log.LoggerFactory;
060    import org.deegree.framework.util.CharsetUtils;
061    import org.deegree.framework.util.StringTools;
062    import org.deegree.framework.xml.NamespaceContext;
063    import org.deegree.framework.xml.XMLParsingException;
064    import org.deegree.framework.xml.XMLTools;
065    import org.deegree.model.crs.CRSFactory;
066    import org.deegree.model.crs.CoordinateSystem;
067    import org.deegree.model.crs.UnknownCRSException;
068    import org.deegree.model.spatialschema.Envelope;
069    import org.deegree.model.spatialschema.GMLGeometryAdapter;
070    import org.deegree.model.spatialschema.GeometryException;
071    import org.deegree.model.spatialschema.GeometryFactory;
072    import org.deegree.model.spatialschema.Point;
073    import org.deegree.ogcbase.CommonNamespaces;
074    import org.deegree.ogcbase.InvalidGMLException;
075    import org.deegree.ogcwebservices.InconsistentRequestException;
076    import org.deegree.ogcwebservices.wms.InvalidFormatException;
077    import org.deegree.ogcwebservices.wms.operation.GetMap;
078    import org.deegree.ogcwebservices.wms.operation.GetMap.Layer;
079    import org.w3c.dom.Element;
080    import org.w3c.dom.Node;
081    
082    /**
083     * This interface describes the access to the parameters of a PrintMap request. It is expected that
084     * there are two kinds of request. The first is the 'normal' HTTP GET request with name-value-pair
085     * enconding and the second is a HTTP POST request containing a SLD. It is possible to access the
086     * values of a HTTP GET request throught its bean accessor methods. The request shall be mapped to a
087     * SLD data structure, accessible using the <code>getSLD()</code> method.
088     * 
089     * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh</a>
090     * @version 2.0
091     */
092    public class PrintMap extends WMPSRequestBase implements Serializable {
093    
094        private static final long serialVersionUID = 6898492018448337645L;
095    
096        private static final ILogger LOG = LoggerFactory.getLogger( PrintMap.class );
097    
098        private static final NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
099    
100        private List<Layer> layers;
101    
102        private String srs;
103    
104        private Envelope boundingBox;
105    
106        private Point center;
107    
108        private int scaleDenominator = -1;
109    
110        private boolean transparent;
111    
112        private Color bgColor;
113    
114        private String title;
115    
116        private String copyright;
117    
118        private boolean legend;
119    
120        private boolean scaleBar;
121    
122        private String note;
123    
124        private String template;
125    
126        private String emailaddress;
127    
128        private Timestamp timestamp;
129    
130        private TextArea[] textAreas;
131    
132        /**
133         * Create a new PrintMap instance.
134         * 
135         * @param id
136         * @param version
137         * @param layers
138         * @param srs
139         * @param boundingBox
140         * @param center
141         * @param scaleDenominator
142         * @param transparent
143         * @param bgColor
144         * @param title
145         * @param copyright
146         * @param legend
147         * @param scaleBar
148         * @param note
149         * @param template
150         * @param emailaddress
151         * @param timestamp
152         * @param textAreas
153         * @param vendorSpecificParameter
154         */
155        PrintMap( String id, String version, Layer[] layers, String srs, Envelope boundingBox, Point center,
156                  int scaleDenominator, boolean transparent, Color bgColor, String title, String copyright, boolean legend,
157                  boolean scaleBar, String note, String template, String emailaddress, Timestamp timestamp,
158                  TextArea[] textAreas, Map<String, String> vendorSpecificParameter ) {
159    
160            super( version, id, vendorSpecificParameter );
161    
162            setLayers( layers );
163            this.srs = srs;
164            this.boundingBox = boundingBox;
165            this.center = center;
166            this.scaleDenominator = scaleDenominator;
167            this.transparent = transparent;
168            this.bgColor = bgColor;
169            this.title = title;
170            this.copyright = copyright;
171            this.legend = legend;
172            this.scaleBar = scaleBar;
173            this.note = note;
174            this.template = template;
175            this.emailaddress = emailaddress;
176            setTimestamp( timestamp );
177            this.textAreas = textAreas;
178        }
179    
180        /**
181         * Set the time stamp.
182         * 
183         * @param timestamp
184         */
185        private void setTimestamp( Timestamp timestamp ) {
186    
187            if ( timestamp != null ) {
188                this.timestamp = timestamp;
189            } else {
190                this.timestamp = setCurrentTime();
191            }
192        }
193    
194        /**
195         * Sets the Current System Time where the request was recieved.
196         * 
197         * @return Date
198         */
199        private static Timestamp setCurrentTime() {
200            long now = System.currentTimeMillis();
201            return new Timestamp( now );
202        }
203    
204        /**
205         * The required LAYERS parameter lists the map layer(s) to be returned by this PrintMapRequest
206         * request. The value of the LAYERS parameter is a comma-separated list of one or more valid
207         * layer names. Allowed layer names are the character data content of any <Layer><Name> element
208         * in the Capabilities XML.
209         * <p>
210         * </p>
211         * A WMS shall render the requested layers by drawing the leftmost in the list bottommost, the
212         * next one over that, and so on.
213         * <p>
214         * </p>
215         * Each layer is associated to a style. Styles are also is encoded as a comma- seperated list
216         * within the PrintMapRequest request.
217         * <p>
218         * </p>
219         * The required STYLES parameter lists the style in which each layer is to be rendered. There is
220         * a one-to-one correspondence between the values in the LAYERS parameter and the values in the
221         * STYLES parameter. Because of this layer-style combinations are returned coupled within an
222         * array of Layer- objects. Each map in the list of LAYERS is drawn using the corresponding
223         * style in the same position in the list of STYLES. Each style Name shall be one that was
224         * defined in the <Name> element of a <Style> element that is either directly contained within,
225         * or inherited by, the associated <Layer> element in Capabilities XML.
226         * 
227         * @return The required LAYERS
228         */
229        public Layer[] getLayers() {
230            return this.layers.toArray( new Layer[this.layers.size()] );
231        }
232    
233        /**
234         * adds the <Layer>
235         * 
236         * @param layer
237         */
238        protected void addLayers( Layer layer ) {
239            this.layers.add( layer );
240        }
241    
242        /**
243         * sets the <Layer>
244         * 
245         * @param layers
246         *            a set of layer
247         */
248        private void setLayers( Layer[] layers ) {
249            this.layers = new ArrayList<Layer>( layers.length );
250            this.layers.clear();
251            if ( layers != null ) {
252                this.layers = Arrays.asList( layers );
253            }
254        }
255    
256        /**
257         * creates a <tt>PrintMapRequest</tt> from its XML representation as defined in the
258         * specification.
259         * 
260         * @param root
261         *            Element
262         * @return an instance of <tt>PrintMapRequest</tt>
263         * @throws InconsistentRequestException
264         * @throws XMLParsingException
265         */
266        public static PrintMap create( Element root )
267                                throws InconsistentRequestException, XMLParsingException {
268    
269            LOG.logInfo( "Validating PrintMapRequest request." );
270            // Validation
271            if ( !root.getLocalName().equals( "PrintMap" ) ) {
272                StringBuffer sb = new StringBuffer( 50 );
273                sb.append( "Unable to create a 'PrintMapRequest' operation for node '" );
274                sb.append( root.getLocalName() + "'. Please check the node to be parsed." );
275                throw new InconsistentRequestException( sb.toString() );
276            }
277            // VERSION
278            String version;
279            try {
280                version = XMLTools.getRequiredAttrValue( "version", null, root );
281            } catch ( XMLParsingException e ) {
282                throw new XMLParsingException( "Error parsing required attribute parameter 'Version'. " + e.getMessage() );
283            }
284    
285            // LAYERS & STYLES
286            List<Layer> layerList = new ArrayList<Layer>();
287            List layerElements = null;
288            try {
289                layerElements = XMLTools.getNodes( root, "deegreewmps:Layers", nsContext );
290            } catch ( XMLParsingException e ) {
291                throw new XMLParsingException( "Error parsing required parameter 'Layer(s)'. " + e.getMessage() );
292            }
293    
294            for ( int i = 0; i < layerElements.size(); i++ ) {
295                Element layer = (Element) layerElements.get( i );
296                List namedLayers = null;
297                try {
298                    namedLayers = XMLTools.getNodes( layer, "sld:NamedLayer", nsContext );
299                    layerList = createLayers( namedLayers, layerList );
300                } catch ( XMLParsingException e ) {
301                    throw new XMLParsingException( "Error parsing parameter 'NamedLayer'." );
302                }
303                List userLayers = null;
304                try {
305                    userLayers = XMLTools.getNodes( layer, "sld:UserLayer", nsContext );
306                    layerList = createLayers( userLayers, layerList );
307                } catch ( XMLParsingException e ) {
308                    throw new XMLParsingException( "Error parsing  parameter 'UserLayer'." );
309                }
310                if ( ( layerList == null ) || ( layerList.size() == 0 ) ) {
311                    throw new InconsistentRequestException( "Atleast one 'NamedLayer' or one "
312                                                            + "'UserLayer' has to be specified." );
313                }
314            }
315            Layer[] layers = layerList.toArray( new Layer[layerList.size()] );
316    
317            // BBOX
318            Element bbox = null;
319            String srsName = null;
320            Envelope boundingBox = null;
321            try {
322                bbox = (Element) XMLTools.getNode( root, "gml:Envelope", nsContext );
323                if ( bbox != null ) {
324                    try {
325                        srsName = XMLTools.getAttrValue( bbox, null, "srsName", null );
326                        boundingBox = GMLGeometryAdapter.wrapBox( bbox, null );
327                    } catch ( InvalidGMLException e ) {
328                        throw new XMLParsingException( "Error creating a bounding box for the " + "'BBOX' parameter." );
329                    } catch ( UnknownCRSException e ) {
330                        throw new InconsistentRequestException( e.getMessage() );
331                    }
332                }
333            } catch ( XMLParsingException e ) {
334                throw new XMLParsingException( "Error parsing optional parameter 'BoundingBox'. " + e.getMessage() );
335            }
336    
337            // Center
338            Point center = null;
339            try {
340                Element centerElement = (Element) XMLTools.getNode( root, "deegreewmps:Center", nsContext );
341                if ( centerElement != null ) {
342                    try {
343                        srsName = XMLTools.getAttrValue( centerElement, null, "srsName", null );
344                        center = (Point) GMLGeometryAdapter.wrap( centerElement, null );
345                    } catch ( GeometryException e ) {
346                        throw new XMLParsingException( "Error creating a Point for the 'Center' " + "parameter. "
347                                                       + e.getMessage() );
348                    }
349                }
350            } catch ( XMLParsingException e ) {
351                throw new XMLParsingException( "Error parsing optional parameter 'Center'. " + e.getMessage() );
352            }
353    
354            // ScaleDenominator
355            int scaleDenominator = -1;
356            try {
357                scaleDenominator = XMLTools.getNodeAsInt( root, "deegreewmps:ScaleDenominator", nsContext, -1 );
358            } catch ( XMLParsingException e ) {
359                throw new XMLParsingException( "Error parsing optional parameter 'Center'. " + e.getMessage() );
360            }
361    
362            if ( boundingBox == null ) {
363                if ( center == null ) {
364                    throw new InconsistentRequestException( "Both 'BoundingBox' and 'Center' are not specified. Either of "
365                                                            + "the two must be set. Both values cannot be null" );
366                }
367                if ( scaleDenominator == -1 ) {
368                    throw new InconsistentRequestException( "Scale Denominator must be specified if the Bounding Box has "
369                                                            + "not been specified. Please check the 'SCALEDENOMINATOR' "
370                                                            + "parameter." );
371                }
372            }
373    
374            // TRANSPARENT
375            boolean transparent = XMLTools.getNodeAsBoolean( root, "deegreewmps:Transparent", nsContext, false );
376    
377            // BGCOLOR
378            Color bgColor = null;
379            String colorstring = XMLTools.getNodeAsString( root, "deegreewmps:BGColor", nsContext, null );
380            if ( colorstring == null ) {
381                bgColor = Color.WHITE;
382            } else {
383                try {
384                    bgColor = Color.decode( colorstring );
385                } catch ( Exception e ) {
386                    throw new InconsistentRequestException( "Error parsing 'BGCOLOR' parameter. The color '" + colorstring
387                                                            + "' is not a hexadecimal definition of a valid color. "
388                                                            + e.getMessage() );
389                }
390            }
391    
392            boolean legend = XMLTools.getNodeAsBoolean( root, "deegreewmps:Legend", nsContext, false );
393    
394            boolean scaleBar = XMLTools.getNodeAsBoolean( root, "deegreewmps:ScaleBar", nsContext, false );
395    
396            String template = XMLTools.getNodeAsString( root, "deegreewmps:Template", nsContext, "default" );
397    
398            String emailAdd = XMLTools.getNodeAsString( root, "deegreewmps:EMailAddress", nsContext, null );
399    
400            List list = XMLTools.getNodes( root, "deegreewmps:TextAreas/deegreewmps:TextArea", nsContext );
401    
402            TextArea[] textAreas = null;
403            String title = null;
404            String copyright = null;
405            String note = null;
406            if ( list != null ) {
407                textAreas = new TextArea[list.size()];
408                for ( int i = 0; i < list.size(); i++ ) {
409                    Node textArea = (Node) list.get( i );
410                    String name = XMLTools.getRequiredNodeAsString( textArea, "deegreewmps:Name", nsContext );
411                    String value = XMLTools.getRequiredNodeAsString( textArea, "deegreewmps:Text", nsContext );
412                    if ( name.equalsIgnoreCase( "TITLE" ) ) {
413                        title = value;
414                    }
415                    if ( name.equalsIgnoreCase( "COPYRIGHT" ) ) {
416                        copyright = value;
417                    }
418                    if ( name.equalsIgnoreCase( "NOTE" ) ) {
419                        note = value;
420                    }
421                    textAreas[i] = new TextArea( name, value );
422    
423                }
424            }
425            Map<String, String> vendorSpecificParameter = getVendorSpecificParameter( root );
426    
427            String id = "" + System.currentTimeMillis();
428    
429            LOG.logInfo( "Created PrintMap request request with id '" + id + "'." );
430    
431            return new PrintMap( id, version, layers, srsName, boundingBox, center, scaleDenominator, transparent, bgColor,
432                                 title, copyright, legend, scaleBar, note, template, emailAdd, setCurrentTime(), textAreas,
433                                 vendorSpecificParameter );
434    
435        }
436    
437        /**
438         * Returns the vendorspecific parameters as a map. Currently handles only the 'session id'
439         * 
440         * @param root
441         * @return Map
442         */
443        private static Map<String, String> getVendorSpecificParameter( Element root ) {
444    
445            Map<String, String> vendorspecific = new HashMap<String, String>();
446    
447            String sessionID = XMLTools.getAttrValue( root, null, "sessionID", null );
448            if ( sessionID != null ) {
449                LOG.logInfo( "vendor specific parameter 'sessionid' retrieved" );
450                vendorspecific.put( "SESSIONID", sessionID );
451            }
452    
453            return vendorspecific;
454        }
455    
456        /**
457         * Create Layer objects for each of namedLayer and userLayer.
458         * 
459         * @param layerNodes
460         * @param layers
461         * @return List list of layer objects
462         * @throws XMLParsingException
463         */
464        private static List<Layer> createLayers( List layerNodes, List<Layer> layers )
465                                throws XMLParsingException {
466    
467            if ( layerNodes != null ) {
468                for ( int i = 0; i < layerNodes.size(); i++ ) {
469                    Node layerNode = (Node) layerNodes.get( i );
470                    try {
471                        String layerName = XMLTools.getRequiredNodeAsString( layerNode, "sld:Name", nsContext );
472                        String styleName = XMLTools.getRequiredNodeAsString( layerNode, "sld:NamedStyle/sld:Name",
473                                                                             nsContext );
474                        layers.add( new Layer( layerName, styleName ) );
475                    } catch ( XMLParsingException e ) {
476                        throw new XMLParsingException( "Error creating a Layer from the Node '" + layerNode.getNodeName()
477                                                       + "'. " + e.getMessage() );
478                    }
479                }
480            }
481    
482            return layers;
483        }
484    
485        /**
486         * creates a <tt>PrintMapRequest</tt> from a <tt>HashMap</tt> that contains the request
487         * parameters as key-value-pairs. Keys are expected to be in upper case notation.
488         * 
489         * @param model
490         *            <tt>HashMap</tt> containing the request parameters
491         * @return an instance of <tt>PrinttMapRequest</tt>
492         * @throws InconsistentRequestException
493         */
494        public static PrintMap create( Map<String, String> model )
495                                throws InconsistentRequestException {
496    
497            retrieveRequestParameter( model );
498    
499            String version = retrieveVersionParameter( model );
500    
501            Layer[] layers = retrieveLayerAndStyleParameters( model );
502    
503            String srs = retrieveSRSParameter( model );
504    
505            Envelope boundingBox = null;
506            Point center = null;
507            try {
508                boundingBox = retrieveBBOXParameter( model, srs );
509                center = retrieveCenterParameter( model, srs );
510            } catch ( UnknownCRSException e ) {
511                throw new InconsistentRequestException( e.getMessage() );
512            }
513    
514            int scaleDenominator = retrieveScaleDenominatorParameter( model );
515    
516            if ( boundingBox == null ) {
517                if ( center == null ) {
518                    throw new InconsistentRequestException( "Both 'BoundingBox' and 'Center' are not specified. Either of "
519                                                            + "the 2 must be set.  Both values cannot be null" );
520                }
521                if ( scaleDenominator == -1 ) {
522                    throw new InconsistentRequestException( "Scale Denominator must be specified if the Bounding Box has "
523                                                            + "not been specified. Please check the 'SCALEDENOMINATOR' "
524                                                            + "parameter." );
525                }
526            }
527    
528            boolean transparent = retrieveTransparentParameter( model );
529    
530            Color bgColor = retrieveBGColorParameter( model );
531    
532            String title = retrieveTitleParameter( model );
533    
534            String copyRightNote = retrieveCopyrightParameter( model );
535    
536            boolean legend = retrieveLegendParameter( model );
537    
538            boolean scaleBar = retrieveScaleBarParameter( model );
539    
540            String note = retrieveNoteParameter( model );
541    
542            String template = retrieveTemplateParameter( model );
543    
544            String emailaddress = retrieveEmailParameter( model );
545    
546            TextArea[] textAreas = retrieveTextAreas( model );
547    
548            Map<String, String> vendorSpecificParameter = model;
549    
550            String id = "" + System.currentTimeMillis();
551    
552            return new PrintMap( id, version, layers, srs, boundingBox, center, scaleDenominator, transparent, bgColor,
553                                 title, copyRightNote, legend, scaleBar, note, template, emailaddress, setCurrentTime(),
554                                 textAreas, vendorSpecificParameter );
555    
556        }
557    
558        /**
559         * Retrieve the Text Areas to be displayed on the PDF output file. Extract the comma seperated
560         * list of name, value pairs. The name and value should be seperated with a ':'. E.g.->
561         * name:value,name:value,name:value
562         * 
563         * @param model
564         * @return TextArea
565         */
566        private static TextArea[] retrieveTextAreas( Map model ) {
567    
568            List<TextArea> texts = new ArrayList<TextArea>();
569            if ( model.containsKey( "TEXTAREAS" ) ) {
570                String textstring = (String) model.remove( "TEXTAREAS" );
571                if ( textstring != null ) {
572                    String[] nameValue = StringTools.toArray( textstring, ",", true );
573                    if ( nameValue != null ) {
574                        for ( int i = 0; i < nameValue.length; i++ ) {
575                            String tmp = nameValue[i].trim();
576                            int idx = tmp.indexOf( ":" );
577                            if ( idx != -1 ) {
578                                String name = tmp.substring( 0, idx );
579                                String value = tmp.substring( idx + 1, tmp.length() );
580                                if ( ( name != null ) && ( value != null ) ) {
581                                    TextArea area = new TextArea( name.toUpperCase(), value );
582                                    texts.add( area );
583                                }
584                            }
585                        }
586                    }
587                }
588            }
589    
590            return texts.toArray( new TextArea[texts.size()] );
591    
592        }
593    
594        /**
595         * Parse 'Template' Parameter.
596         * 
597         * @param model
598         * @return String
599         * @throws InconsistentRequestException
600         */
601        private static String retrieveTemplateParameter( Map model )
602                                throws InconsistentRequestException {
603    
604            String templatestring = null;
605            if ( model.containsKey( "TEMPLATE" ) ) {
606                templatestring = (String) model.remove( "TEMPLATE" );
607    
608            }
609            if ( templatestring == null ) {
610                throw new InconsistentRequestException( "No Template defined. A Template name has to be specified "
611                                                        + "along with the 'PrintMap' request." );
612            }
613    
614            return templatestring;
615        }
616    
617        /**
618         * Retrieve Email parameter
619         * 
620         * @param model
621         * @return String
622         * @throws InconsistentRequestException
623         */
624        private static String retrieveEmailParameter( Map model )
625                                throws InconsistentRequestException {
626    
627            String email = null;
628            if ( model.containsKey( "EMAIL" ) ) {
629                email = (String) model.remove( "EMAIL" );
630            }
631    
632            if ( email == null ) {
633                throw new InconsistentRequestException( "EMail parameter must be set." );
634            }
635    
636            return email;
637        }
638    
639        /**
640         * Parse 'Note' Parameter.
641         * 
642         * @param model
643         * @return String
644         */
645        private static String retrieveNoteParameter( Map model ) {
646    
647            String note = null;
648            if ( model.containsKey( "NOTE" ) ) {
649                note = (String) model.remove( "NOTE" );
650            }
651    
652            return note;
653        }
654    
655        /**
656         * Parse 'ScaleBar' Parameter.
657         * 
658         * @param model
659         * @return boolean
660         * @throws InconsistentRequestException
661         */
662        private static boolean retrieveScaleBarParameter( Map model )
663                                throws InconsistentRequestException {
664    
665            boolean showScaleBar = false;
666            if ( model.containsKey( "SCALEBAR" ) ) {
667                String scaleBar = (String) model.remove( "SCALEBAR" );
668                if ( scaleBar == null ) {
669                    showScaleBar = false;
670                } else if ( scaleBar.equalsIgnoreCase( "True" ) ) {
671                    showScaleBar = true;
672                } else if ( scaleBar.equalsIgnoreCase( "False" ) ) {
673                    showScaleBar = false;
674                } else {
675                    throw new InconsistentRequestException( "The 'ScaleBar' parameter can only have 'True', 'False' "
676                                                            + "values. Here it is '" + scaleBar + "'." );
677                }
678            }
679    
680            return showScaleBar;
681        }
682    
683        /**
684         * Parse 'Legend' Parameter.
685         * 
686         * @param model
687         * @return boolean
688         * @throws InconsistentRequestException
689         */
690        private static boolean retrieveLegendParameter( Map model )
691                                throws InconsistentRequestException {
692    
693            boolean showLegend = false;
694            if ( model.containsKey( "LEGEND" ) ) {
695                String legend = (String) model.remove( "LEGEND" );
696                if ( legend == null ) {
697                    showLegend = false;
698                } else if ( legend.equalsIgnoreCase( "True" ) ) {
699                    showLegend = true;
700                } else if ( legend.equalsIgnoreCase( "False" ) ) {
701                    showLegend = false;
702                } else {
703                    throw new InconsistentRequestException( "The 'Legend' parameter can only have 'True', 'False' values. "
704                                                            + "Here it is '" + legend + "'." );
705                }
706            }
707    
708            return showLegend;
709    
710        }
711    
712        /**
713         * Parse 'Copyright' Parameter.
714         * 
715         * @param model
716         * @return String
717         */
718        private static String retrieveCopyrightParameter( Map model ) {
719    
720            String copyright = null;
721            if ( model.containsKey( "COPYRIGHT" ) ) {
722                copyright = (String) model.remove( "COPYRIGHT" );
723            }
724    
725            return copyright;
726        }
727    
728        /**
729         * Parse 'Title' Parameter.
730         * 
731         * @param model
732         * @return String
733         */
734        private static String retrieveTitleParameter( Map model ) {
735    
736            String title = null;
737            if ( model.containsKey( "TITLE" ) ) {
738                title = (String) model.remove( "TITLE" );
739            }
740    
741            return title;
742        }
743    
744        /**
745         * Parse 'BGColor' Parameter.
746         * 
747         * @param model
748         * @return Color
749         * @throws InconsistentRequestException
750         */
751        private static Color retrieveBGColorParameter( Map model )
752                                throws InconsistentRequestException {
753    
754            Color bgColor = Color.WHITE;
755            if ( model.containsKey( "BGCOLOR" ) ) {
756                String colorstring = (String) model.remove( "BGCOLOR" );
757                if ( ( colorstring != null ) || ( colorstring == "" ) ) {
758                    try {
759                        bgColor = Color.decode( colorstring );
760                    } catch ( Exception e ) {
761                        throw new InconsistentRequestException( "Error parsing 'BGCOLOR' parameter. The color '"
762                                                                + colorstring + "' is not a hexadecimal "
763                                                                + "definition of a valid color. " + e.getMessage() );
764                    }
765                }
766            }
767    
768            return bgColor;
769        }
770    
771        /**
772         * Parse 'Transparent' Parameter.
773         * 
774         * @param model
775         * @return boolean
776         * @throws InconsistentRequestException
777         */
778        private static boolean retrieveTransparentParameter( Map model )
779                                throws InconsistentRequestException {
780    
781            boolean isTransparent = false;
782            if ( model.containsKey( "TRANSPARENT" ) ) {
783                String transparent = (String) model.remove( "TRANSPARENT" );
784                if ( transparent == null ) {
785                    isTransparent = false;
786                } else if ( transparent.equalsIgnoreCase( "True" ) ) {
787                    isTransparent = true;
788                } else if ( transparent.equalsIgnoreCase( "False" ) ) {
789                    isTransparent = false;
790                } else {
791                    throw new InconsistentRequestException( "The 'Transparent' parameter can only have "
792                                                            + "'True', 'False' values. Here it has '" + transparent + "'." );
793                }
794            }
795    
796            return isTransparent;
797        }
798    
799        /**
800         * Parse 'ScaleDenominator' Parameter.
801         * 
802         * @param model
803         * @return String
804         */
805        private static int retrieveScaleDenominatorParameter( Map model ) {
806    
807            int scale = -1;
808            if ( model.containsKey( "SCALE" ) ) {
809                String value = (String) model.remove( "SCALE" );
810                if ( value != null ) {
811                    scale = Integer.parseInt( value );
812                }
813            }
814    
815            return scale;
816    
817        }
818    
819        /**
820         * Parse 'Center' Parameter and create a point.
821         * 
822         * @param model
823         * @param srs
824         * @return Point to represent the x,y coordinates.
825         * @throws InconsistentRequestException
826         * @throws UnknownCRSException
827         */
828        private static Point retrieveCenterParameter( Map model, String srs )
829                                throws InconsistentRequestException, UnknownCRSException {
830    
831            Point center = null;
832            if ( model.containsKey( "CENTER" ) ) {
833                String centerstring = (String) model.remove( "CENTER" );
834    
835                String[] values = centerstring.split( "," );
836                if ( values.length != 2 ) {
837                    throw new InconsistentRequestException( "Centre should be defined as a Point with 'X' and 'Y' "
838                                                            + "values. The current length is '" + values.length
839                                                            + "'. It should " + "be '2'." );
840                }
841                double x = -1;
842                double y = -1;
843                try {
844                    x = Double.parseDouble( values[0] );
845                    y = Double.parseDouble( values[1] );
846                } catch ( Exception e ) {
847                    throw new InconsistentRequestException( "Error converting 'X','Y' coordinates in the request "
848                                                            + "parameter 'CENTER' to double. Please check the "
849                                                            + "values entered." );
850                }
851    
852                CoordinateSystem crs = CRSFactory.create( srs );
853                center = GeometryFactory.createPoint( x, y, crs );
854    
855            }
856    
857            return center;
858        }
859    
860        /**
861         * Parse 'Envelope' Parameter and create an envelope.
862         * 
863         * @param model
864         * @param srs
865         * @return Envelope
866         * @throws InconsistentRequestException
867         * @throws InvalidFormatException
868         * @throws UnknownCRSException
869         */
870        private static Envelope retrieveBBOXParameter( Map model, String srs )
871                                throws InconsistentRequestException, InvalidFormatException, UnknownCRSException {
872    
873            Envelope bbox = null;
874    
875            if ( model.containsKey( "BBOX" ) ) {
876                String boxstring = (String) model.remove( "BBOX" );
877                StringTokenizer st = new StringTokenizer( boxstring, "," );
878                String s = st.nextToken().replace( ' ', '+' );
879                double minx = Double.parseDouble( s );
880                s = st.nextToken().replace( ' ', '+' );
881                double miny = Double.parseDouble( s );
882                s = st.nextToken().replace( ' ', '+' );
883                double maxx = Double.parseDouble( s );
884                s = st.nextToken().replace( ' ', '+' );
885                double maxy = Double.parseDouble( s );
886    
887                if ( minx >= maxx ) {
888                    throw new InvalidFormatException( "minx must be lesser than maxx" );
889                }
890    
891                if ( miny >= maxy ) {
892                    throw new InvalidFormatException( "miny must be lesser than maxy" );
893                }
894    
895                CoordinateSystem crs = CRSFactory.create( srs );
896                bbox = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, crs );
897            }
898    
899            return bbox;
900        }
901    
902        /**
903         * Parse 'SRS' Parameter.
904         * 
905         * @param model
906         * @return String
907         * @throws InconsistentRequestException
908         */
909        private static String retrieveSRSParameter( Map model )
910                                throws InconsistentRequestException {
911    
912            String srs = null;
913            if ( model.containsKey( "SRS" ) ) {
914                srs = (String) model.remove( "SRS" );
915            }
916            if ( srs == null ) {
917                throw new InconsistentRequestException( "SRS-value must be set" );
918            }
919    
920            return srs;
921        }
922    
923        /**
924         * Parse 'Layer' and 'Style' Parameter.
925         * 
926         * @param model
927         * @return Layer[]
928         * @throws InconsistentRequestException
929         */
930        private static Layer[] retrieveLayerAndStyleParameters( Map model )
931                                throws InconsistentRequestException {
932    
933            String layersstring = null;
934            if ( model.containsKey( "LAYERS" ) ) {
935                layersstring = (String) model.remove( "LAYERS" );
936            }
937            String stylesstring = null;
938            if ( model.containsKey( "STYLES" ) ) {
939                stylesstring = (String) model.remove( "STYLES" );
940            }
941    
942            // normalize styles parameter
943            if ( ( stylesstring == null ) || stylesstring.trim().equals( "" ) ) {
944                stylesstring = "$DEFAULT";
945            }
946            if ( stylesstring.startsWith( "," ) ) {
947                stylesstring = "$DEFAULT" + stylesstring;
948            }
949            String tmp = ",$DEFAULT,";
950            stylesstring = StringTools.replace( stylesstring, ",,", tmp, true );
951            if ( stylesstring.endsWith( "," ) ) {
952                stylesstring = stylesstring + "$DEFAULT";
953            }
954            List<String> layers = new ArrayList<String>();
955            List<String> styles = new ArrayList<String>();
956            GetMap.Layer[] ls = null;
957            if ( ( layersstring != null ) && !layersstring.trim().equals( "" ) ) {
958                StringTokenizer st = new StringTokenizer( layersstring, "," );
959                int a = 0;
960                while ( st.hasMoreTokens() ) {
961                    String s = st.nextToken();
962                    layers.add( s );
963                }
964                st = new StringTokenizer( stylesstring, "," );
965                for ( int i = 0; i < layers.size(); i++ ) {
966                    styles.add( "$DEFAULT" );
967                }
968                a = 0;
969                while ( st.hasMoreTokens() ) {
970                    String s = st.nextToken();
971                    styles.set( a++, s );
972                }
973    
974                // At last, build up the Layer object
975                ls = new GetMap.Layer[layers.size()];
976    
977                for ( int i = 0; i < layers.size(); i++ ) {
978                    try {
979                        String l = URLDecoder.decode( layers.get( i ), CharsetUtils.getSystemCharset() );
980                        ls[i] = PrintMap.createLayer( l, styles.get( i ) );
981                    } catch ( UnsupportedEncodingException e2 ) {
982                        e2.printStackTrace();
983                    }
984                }
985            }
986    
987            if ( ( ls == null || ls.length == 0 ) ) {
988                throw new InconsistentRequestException( "No layers defined in PrintMapRequest request" );
989            }
990    
991            return ls;
992        }
993    
994        /**
995         * Parse the Request parameter to check if the request is actually a 'PrintMapRequest' request
996         * 
997         * @param model
998         * @throws InconsistentRequestException
999         */
1000        private static void retrieveRequestParameter( Map model )
1001                                throws InconsistentRequestException {
1002    
1003            String request = null;
1004            if ( model.containsKey( "REQUEST" ) ) {
1005                request = (String) model.remove( "REQUEST" );
1006            } else {
1007                throw new InconsistentRequestException( "Unable to create a PrintMapRequest operation. "
1008                                                        + "The request parameter is missing." );
1009            }
1010            if ( request == null || !( request.equals( "PrintMap" ) ) ) {
1011                throw new InconsistentRequestException( "Unable to create a PrintMapRequest operation for request '"
1012                                                        + request + "'." );
1013            }
1014    
1015        }
1016    
1017        /**
1018         * Parse 'Version' Parameter.
1019         * 
1020         * @param model
1021         * @return String version (default=1.0.0)
1022         */
1023        private static String retrieveVersionParameter( Map model ) {
1024    
1025            String version = null;
1026            if ( model.containsKey( "VERSION" ) ) {
1027                version = (String) model.remove( "VERSION" );
1028            }
1029            if ( version == null ) {
1030                /** default value set as per the WMPS draft specifications. */
1031                version = "1.0.0";
1032            }
1033    
1034            return version;
1035        }
1036    
1037        /**
1038         * Get TimeStamp
1039         * 
1040         * @return TimeStamp
1041         */
1042        public Timestamp getTimestamp() {
1043            return this.timestamp;
1044        }
1045    
1046        /**
1047         * Get SRS
1048         * 
1049         * @return String
1050         */
1051        public String getSRS() {
1052            return this.srs;
1053        }
1054    
1055        /**
1056         * Get bounding box
1057         * 
1058         * @return Envelope maybe null
1059         */
1060        public Envelope getBBOX() {
1061            return this.boundingBox;
1062        }
1063    
1064        /**
1065         * Get center
1066         * 
1067         * @return Point maybe null
1068         */
1069        public Point getCenter() {
1070            return this.center;
1071        }
1072    
1073        /**
1074         * Get ScaleDenominator
1075         * 
1076         * @return String maybe null
1077         */
1078        public int getScaleDenominator() {
1079            return this.scaleDenominator;
1080        }
1081    
1082        /**
1083         * Get Transparency
1084         * 
1085         * @return boolean
1086         */
1087        public boolean getTransparent() {
1088            return this.transparent;
1089        }
1090    
1091        /**
1092         * Get BGColor
1093         * 
1094         * @return Color default is White.
1095         */
1096        public Color getBGColor() {
1097            return this.bgColor;
1098        }
1099    
1100        /**
1101         * Get Map Title
1102         * 
1103         * @return String maybe null
1104         */
1105        public String getTitle() {
1106            return this.title;
1107        }
1108    
1109        /**
1110         * Get Copyright
1111         * 
1112         * @return String maybe null
1113         */
1114        public String getCopyright() {
1115            return this.copyright;
1116        }
1117    
1118        /**
1119         * Get Legend
1120         * 
1121         * @return boolean
1122         */
1123        public boolean getLegend() {
1124            return this.legend;
1125        }
1126    
1127        /**
1128         * Get Scale Bar
1129         * 
1130         * @return boolean
1131         */
1132        public boolean getScaleBar() {
1133            return this.scaleBar;
1134        }
1135    
1136        /**
1137         * Get Note (extra descriptive text)
1138         * 
1139         * @return String maybe null
1140         */
1141        public String getNote() {
1142            return this.note;
1143        }
1144    
1145        /**
1146         * Get Template
1147         * 
1148         * @return String
1149         */
1150        public String getTemplate() {
1151            return this.template;
1152        }
1153    
1154        /**
1155         * Get Email Address
1156         * 
1157         * @return String
1158         */
1159        public String getEmailAddress() {
1160            return this.emailaddress;
1161        }
1162    
1163        /**
1164         * Get Text Areas
1165         * 
1166         * @return TextArea[]
1167         */
1168        public TextArea[] getTextAreas() {
1169            return this.textAreas;
1170        }
1171    
1172        /**
1173         * Retrieve ( if available ) the current text area identified by 'name' from the list of text
1174         * areas defined. May return null.
1175         * 
1176         * @param name
1177         * @return TextArea
1178         */
1179        public TextArea getTextArea( String name ) {
1180    
1181            TextArea textArea = null;
1182            if ( this.textAreas != null && this.textAreas.length > 0 ) {
1183                for ( int i = 0; i < this.textAreas.length; i++ ) {
1184                    TextArea tmp = this.textAreas[i];
1185                    if ( tmp.getName().equalsIgnoreCase( name ) ) {
1186                        textArea = tmp;
1187                        break;
1188                    }
1189                }
1190            }
1191    
1192            return textArea;
1193        }
1194    
1195        /**
1196         * creates a Layer object beacuse of the inner class construct.
1197         * 
1198         * @param name
1199         *            the name of the layer
1200         * @param style
1201         *            the corresponding style of the layer
1202         * @return Layer a layer object constaining name and style
1203         */
1204        public static Layer createLayer( String name, String style ) {
1205            return new Layer( name, style );
1206        }
1207    
1208        /**
1209         * Returns a new PrintMap instance.
1210         * 
1211         * @param id
1212         * @param version
1213         * @param layers
1214         * @param srs
1215         * @param bbox
1216         * @param center
1217         * @param scaleDenominator
1218         * @param transparent
1219         * @param bgColor
1220         * @param title
1221         * @param copyright
1222         * @param legend
1223         * @param scaleBar
1224         * @param note
1225         * @param template
1226         * @param emailAddress
1227         * @param timestamp
1228         * @param textAreas
1229         * @param vendorSpecificParameters
1230         * @return PrintMap
1231         */
1232        public static PrintMap create( String id, String version, Layer[] layers, String srs, Envelope bbox, Point center,
1233                                       int scaleDenominator, boolean transparent, Color bgColor, String title,
1234                                       String copyright, boolean legend, boolean scaleBar, String note, String template,
1235                                       String emailAddress, Timestamp timestamp, TextArea[] textAreas,
1236                                       Map<String, String> vendorSpecificParameters ) {
1237    
1238            return new PrintMap( id, version, layers, srs, bbox, center, scaleDenominator, transparent, bgColor, title,
1239                                 copyright, legend, scaleBar, note, template, emailAddress, timestamp, textAreas,
1240                                 vendorSpecificParameters );
1241        }
1242    
1243        /**
1244         * Overwrite the toString() method to export the current request as a readable statement.
1245         * Currently only the id, version and layer names will be given out. TODO the rest
1246         * 
1247         * @return String
1248         */
1249        @Override
1250        public String toString() {
1251    
1252            StringBuffer sb = new StringBuffer( 200 );
1253            sb.append( "id: " );
1254            sb.append( getId() );
1255            sb.append( "\n" );
1256            sb.append( "version: " );
1257            sb.append( getVersion() );
1258            sb.append( "\n" );
1259            if ( this.layers != null ) {
1260                sb.append( "layer(s): " );
1261                for ( int i = 0; i < this.layers.size(); i++ ) {
1262                    sb.append( this.layers.get( i ).getName() );
1263                    sb.append( "\n" );
1264                }
1265            }
1266            return sb.toString();
1267        }
1268    }