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