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