001    //$HeadURL: svn+ssh://melmasry@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/portal/standard/context/control/FullScreenListener.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    
037    package org.deegree.portal.standard.context.control;
038    
039    import java.awt.Rectangle;
040    import java.io.IOException;
041    import java.net.MalformedURLException;
042    import java.util.HashMap;
043    import java.util.Iterator;
044    import java.util.LinkedList;
045    import java.util.List;
046    import java.util.Map;
047    
048    import javax.servlet.http.HttpServletRequest;
049    import javax.servlet.http.HttpSession;
050    import javax.xml.parsers.ParserConfigurationException;
051    import javax.xml.transform.TransformerException;
052    
053    import org.deegree.enterprise.control.FormEvent;
054    import org.deegree.enterprise.control.RPCMember;
055    import org.deegree.enterprise.control.RPCStruct;
056    import org.deegree.enterprise.control.RPCWebEvent;
057    import org.deegree.framework.log.ILogger;
058    import org.deegree.framework.log.LoggerFactory;
059    import org.deegree.framework.util.MapUtils;
060    import org.deegree.framework.util.Parameter;
061    import org.deegree.framework.xml.XMLFragment;
062    import org.deegree.i18n.Messages;
063    import org.deegree.model.crs.CoordinateSystem;
064    import org.deegree.model.spatialschema.Envelope;
065    import org.deegree.model.spatialschema.GeometryFactory;
066    import org.deegree.model.spatialschema.Point;
067    import org.deegree.portal.Constants;
068    import org.deegree.portal.PortalException;
069    import org.deegree.portal.context.ContextException;
070    import org.deegree.portal.context.Frontend;
071    import org.deegree.portal.context.GUIArea;
072    import org.deegree.portal.context.General;
073    import org.deegree.portal.context.GeneralExtension;
074    import org.deegree.portal.context.Module;
075    import org.deegree.portal.context.ViewContext;
076    import org.deegree.portal.context.XMLFactory;
077    import org.xml.sax.SAXException;
078    
079    /**
080     * Convert the current view context to a full screen mode. It hides all the modules except the map
081     * and the toolbar. It also keeps whatever changes were made in the original context to the full
082     * screen context
083     *
084     * @author <a href="mailto:elmasry@lat-lon.de">Moataz Elmasry</a>
085     * @author last edited by: $Author: $
086     *
087     * @version $Revision: $, $Date: 1 Jun 2007 10:12:29$
088     */
089    public class FullScreenListener extends AbstractContextListener {
090    
091        private int width = 0;
092    
093        private Map<String, String> modulesMap = null;
094    
095        /**
096         * the path to where the Web Map Context files are found
097         */
098        public static final String CONTEXTPATH = "WEB-INF/conf/igeoportal";
099    
100        private String xslFileName = null;
101    
102        private HttpSession session = null;
103    
104        /**
105         * A <code>String</code> defining the name of the xsl file that defines the transformation
106         * from a context to html. This must be placed, together with the map context xml and helper xsl
107         * files, under <code>${context-home}/WEB-INF/conf/igeoportal/</code>.
108         */
109        protected static final String DEFAULT_CTXT2HTML = "WEB-INF/conf/igeoportal/context2HTML.xsl";
110    
111        private static final ILogger LOG = LoggerFactory.getLogger( ResetContextListener.class );
112    
113        private static final String DISPLAYED_MODULES = "DISPLAYED_MODULES";
114    
115        private static final String NORMALSCREEN_MAPCONTEXT = "NormalScreenMapContext";
116    
117        /*
118         * (non-Javadoc)
119         *
120         * @see org.deegree.enterprise.control.WebListener#actionPerformed(org.deegree.enterprise.control.FormEvent)
121         */
122        @Override
123        public void actionPerformed( FormEvent event ) {
124    
125            RPCWebEvent rpc = (RPCWebEvent) event;
126    
127            try {
128                validate( rpc );
129                validateCntxtProperties();
130                initialize();
131            } catch ( PortalException e ) {
132                LOG.logError( e.getMessage(), e );
133                e.printStackTrace();
134                gotoErrorPage( e.getMessage() );
135                return;
136            }
137    
138            try {
139                ViewContext fullScreenContext = setToFullScreen( rpc );
140                setCurrentContext( fullScreenContext );
141            } catch ( Exception e ) {
142                LOG.logError( e.getMessage(), e );
143                e.printStackTrace();
144                gotoErrorPage( e.getMessage() );
145                return;
146            }
147        }
148    
149        /**
150         * ValidateRPC looks in the RPCStruct for all needed elements and gotoErrorPage in case an
151         * element is found or wronglz formated, this is useful so that we won't need to check later for
152         * anz variables, simply get them and start Working
153         *
154         * @param rpc
155         * @throws PortalException
156         */
157        protected void validate( RPCWebEvent rpc )
158                                throws PortalException {
159    
160            RPCStruct struct = extractRPCStruct( rpc, 0 );
161    
162            RPCMember layerListRPC = struct.getMember( "layerList" );
163            validateLayerList( layerListRPC );
164    
165            RPCMember bboxRPC = struct.getMember( "boundingBox" );
166            validateBBox( bboxRPC );
167        }
168    
169        /**
170         * This method checks the context.properties file to make sure that all fields needed are
171         * available
172         *
173         */
174        protected void validateCntxtProperties()
175                                throws PortalException {
176    
177            String displayedModules = ContextMessages.getString( DISPLAYED_MODULES );
178            if ( displayedModules == null ) {
179                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_CONFIG_FILE",
180                                                                ContextMessages.getName(), "DISPLAYED_MODULES" ) );
181            }
182    
183            if ( displayedModules.indexOf( "MANDATORY_MODULE_MAP" ) < 0 ) {
184                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_CONFIG_FILE",
185                                                                ContextMessages.getName(), "MANDATORY_MODULE_MAP" ) );
186            }
187    
188            if ( displayedModules.indexOf( "MANDATORY_MODULE_TOOLBAR" ) < 0 ) {
189                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_CONFIG_FILE",
190                                                                ContextMessages.getName(), "MANDATORY_MODULE_TOOLBAR" ) );
191            }
192    
193            String mapName = ContextMessages.getString( "MANDATORY_MODULE_MAP" );
194            if ( mapName == null ) {
195                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_MISSING_MAND_PARAM",
196                                                                ContextMessages.getName(), "MANDATORY_MODULE_MAP" ) );
197            }
198    
199            String toolbarName = ContextMessages.getString( "MANDATORY_MODULE_TOOLBAR" );
200            if ( toolbarName == null ) {
201                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_MISSING_MAND_PARAM",
202                                                                ContextMessages.getName(), "MANDATORY_MODULE_TOOLBAR" ) );
203            }
204    
205            int width = Integer.parseInt( ContextMessages.getString( "MAP_WIDTH" ) );
206            if ( width <= 0 ) {
207                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_CONFIG_FILE",
208                                                                ContextMessages.getName(), "MAP_WIDTH" ) );
209            }
210    
211            String normalScreen = ContextMessages.getString( "NORMAL_SIZE_SCREEN" );
212            if ( normalScreen == null ) {
213                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_CONFIG_FILE",
214                                                                ContextMessages.getName(), "NORMAL_SIZE_SCREEN" ) );
215            }
216        }
217    
218        /**
219         * Initializes the global variables
220         *
221         * @throws PortalException
222         */
223        protected void initialize()
224                                throws PortalException {
225    
226            try {
227                xslFileName = "file://" + getHomePath() + DEFAULT_CTXT2HTML;
228    
229                width = Integer.parseInt( ContextMessages.getString( "MAP_WIDTH" ) );
230                session = ( (HttpServletRequest) this.getRequest() ).getSession();
231    
232                modulesMap = loadModulesNames( DISPLAYED_MODULES );
233            } catch ( Exception e ) {
234                LOG.logError( e.getMessage() );
235                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_INITIALIZE", e ) );
236            }
237        }
238    
239        /**
240         * Applies the modules read from the context.properties file to the current context using the
241         * data from the session on the defaultContext
242         *
243         * @param rpc
244         * @return the view context for full screen
245         * @throws ParserConfigurationException
246         * @throws PortalException
247         * @throws ContextException
248         */
249        protected ViewContext setToFullScreen( RPCWebEvent rpc )
250                                throws ParserConfigurationException, PortalException, ContextException {
251    
252            RPCStruct struct = extractRPCStruct( rpc, 0 );
253            ViewContext tempContext = applyLayers( struct );
254    
255            try {
256                XMLFragment xmlActualContext = XMLFactory.export( tempContext );
257                // Saving the original Context
258                session.setAttribute( NORMALSCREEN_MAPCONTEXT, xmlActualContext );
259            } catch ( ParserConfigurationException e ) {
260                LOG.logError( e.getMessage() );
261                throw new ParserConfigurationException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_TRANSFORM" ) );
262            }
263            return createFullScreenContext( struct, tempContext );
264        }
265    
266        /**
267         * Takes in viewContext converts it to a html of the viewcontext and display it
268         *
269         * @param vc the Viewcontext after convert
270         * @throws ParserConfigurationException
271         * @throws TransformerException
272         * @throws SAXException
273         * @throws MalformedURLException
274         * @throws IOException
275         */
276        protected void setCurrentContext( ViewContext vc )
277                                throws ParserConfigurationException, TransformerException, SAXException,
278                                MalformedURLException, IOException {
279    
280            try {
281                XMLFragment xml = XMLFactory.export( vc );
282                String newHtml = transformToHtmlMapContext( xml, xslFileName );
283                session.setAttribute( ContextSwitchListener.NEW_CONTEXT_HTML, newHtml );
284            } catch ( ParserConfigurationException e ) {
285                LOG.logError( e.getMessage() );
286                throw new ParserConfigurationException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_TRANSFORM" ) );
287            }
288        }
289    
290        /**
291         * Gets the current map context from the session and applies the current layers to it.
292         *
293         * @param struct
294         * @return the viewContext after applying the current layers to it
295         * @throws PortalException
296         */
297        private ViewContext applyLayers( RPCStruct struct )
298                                throws PortalException {
299    
300            ViewContext tempContext = null;
301            try {
302                String sessionId = (String) session.getAttribute( "SESSIONID" );
303                ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT );
304                tempContext = vc.clone( null, sessionId );
305    
306                // Applying the layer setting to the current context
307                RPCMember[] layerList = ( (RPCStruct) struct.getMember( "layerList" ).getValue() ).getMembers();
308                changeLayerList( tempContext, layerList );
309    
310            } catch ( PortalException e ) {
311                // throw new PortalException( Messages.getMessage(
312                // "IGEO_STD_CNTXT_ERROR_EMPTY_LAYERLIST" ) );
313                throw e;
314            } catch ( Exception e ) {
315                LOG.logError( e.getMessage() );
316                throw new PortalException( Messages.getMessage( "IGEO_STD_ERROR_UNKNOWN", e ) );
317            }
318            return tempContext;
319        }
320    
321        /**
322         * Validates the data in the struct to make sure it contains a valid layerList
323         *
324         * @param layerListRPCMember
325         * @throws PortalException
326         */
327        protected void validateLayerList( RPCMember layerListRPCMember )
328                                throws PortalException {
329    
330            if ( layerListRPCMember == null || layerListRPCMember.getValue() == null ) {
331                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_MISSING_RPC_MEMBER", "layerList" ) );
332            }
333    
334            RPCMember[] layerList = null;
335            try {
336                layerList = ( (RPCStruct) layerListRPCMember.getValue() ).getMembers();
337            } catch ( Exception e ) {
338                LOG.logError( e.getMessage() );
339                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_WRONG_RPC_MEMBER_VALUE", "layerList" ) );
340            }
341    
342            if ( layerList == null || layerList.length < 1 ) {
343                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_EMPTY_LAYERLIST", "layerList" ) );
344            }
345        }
346    
347        /**
348         * Validates the data in the struct to make sure it contains a valid bbox
349         *
350         * @param bboxRPCMember
351         * @throws PortalException
352         */
353        protected void validateBBox( RPCMember bboxRPCMember )
354                                throws PortalException {
355    
356            if ( bboxRPCMember == null || bboxRPCMember.getValue() == null ) {
357                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_MISSING_RPC_MEMBER", "boundingBox" ) );
358            }
359    
360            RPCStruct bboxStruct = (RPCStruct) bboxRPCMember.getValue();
361            Double minx = null;
362            Double miny = null;
363            Double maxx = null;
364            Double maxy = null;
365    
366            RPCMember mem = bboxStruct.getMember( Constants.RPC_BBOXMINX );
367            if ( mem != null ) {
368                minx = (Double) mem.getValue();
369            }
370            mem = bboxStruct.getMember( Constants.RPC_BBOXMINY );
371            if ( mem != null ) {
372                miny = (Double) mem.getValue();
373            }
374            mem = bboxStruct.getMember( Constants.RPC_BBOXMAXX );
375            if ( mem != null ) {
376                maxx = (Double) mem.getValue();
377            }
378            mem = bboxStruct.getMember( Constants.RPC_BBOXMAXY );
379            if ( mem != null ) {
380                maxy = (Double) mem.getValue();
381            }
382    
383            if ( minx == null || maxx == null || miny == null || maxy == null ) {
384                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_WRONG_RPC_MEMBER_VALUE", "boundingBox" ) );
385            }
386            if ( minx >= maxx || miny >= maxy ) {
387                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_BBOX_BOUNDARIES" ) );
388            }
389        }
390    
391        /**
392         * Extracts shown modules names from the context.properties file
393         *
394         * @param selectedModules
395         *            The modules names key as written in the context.properties file
396         * @return Map contains all the used modules
397         */
398        private Map<String, String> loadModulesNames( String selectedModules ) {
399    
400            String modulesNames = ContextMessages.getString( selectedModules );
401            Map<String, String> modules = new HashMap<String, String>();
402            String[] tmpNames = modulesNames.split( ";" );
403            for ( int i = 0; i < tmpNames.length; i++ ) {
404                String value = ContextMessages.getString( tmpNames[i] );
405                if ( tmpNames[i] != null && value != null ) {
406                    modules.put( tmpNames[i], value );
407                }
408            }
409            return modules;
410        }
411    
412        /**
413         * Sets the given context to FullScreen mode.
414         *
415         * @param struct the rpc struct containing the current bbox envelope
416         * @param vc view context with the layers
417         * @return The context transformed to the fullScreen size
418         * @throws ContextException
419         * @throws PortalException
420         */
421        protected ViewContext createFullScreenContext( RPCStruct struct, ViewContext vc )
422                                throws ContextException, PortalException {
423    
424            RPCStruct bBoxStruct = (RPCStruct) struct.getMember( "boundingBox" ).getValue();
425            Envelope envelope = extractBBox( bBoxStruct, null );
426    
427            String sessionID = (String) session.getAttribute( "SESSIONID" );
428            ViewContext fullscreenContext = null;
429            try {
430                fullscreenContext = vc.clone( null, sessionID );
431            } catch ( Exception e ) {
432                throw new ContextException( "The view context couldn't be cloned" );
433            }
434            General general = fullscreenContext.getGeneral();
435            GeneralExtension extension = general.getExtension();
436            Frontend frontEnd = extension.getFrontend();
437    
438            // The bbox values are changed here
439            Point[] points = general.getBoundingBox();
440            // if there are no 2 points, then nothing happens
441            if ( points.length == 2 ) {
442                Rectangle rect = general.getWindow();
443                rect.width = width; // setting new window width
444                CoordinateSystem crs = points[0].getCoordinateSystem();
445                Envelope bbox = MapUtils.ensureAspectRatio( envelope, rect.width, rect.height );
446    
447                Point[] newPoints = new Point[2];
448                newPoints[0] = GeometryFactory.createPoint( bbox.getMin(), crs );
449                newPoints[1] = GeometryFactory.createPoint( bbox.getMax(), crs );
450    
451                general.setBoundingBox( newPoints );
452            } else {
453                throw new ContextException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_VIEWCONTEXT", "bounding box" ) );
454            }
455    
456            // Here removing all the side modules
457            List<GUIArea> contextAreas = new LinkedList<GUIArea>();
458            // Makign sure we fetched the names right, otherwise we don't have the names to compare with
459    
460            // Adding GuiAreas to a list to do a for loop
461            // instead of accessing them individually
462            GUIArea curArea = null;
463            if ( ( curArea = frontEnd.getNorth() ) != null ) {
464                contextAreas.add( curArea );
465            }
466            if ( ( curArea = frontEnd.getSouth() ) != null ) {
467                contextAreas.add( curArea );
468            }
469            if ( ( curArea = frontEnd.getEast() ) != null ) {
470                contextAreas.add( curArea );
471            }
472            if ( ( curArea = frontEnd.getWest() ) != null ) {
473                contextAreas.add( curArea );
474            }
475            if ( ( curArea = frontEnd.getCenter() ) != null ) {
476                contextAreas.add( curArea );
477            }
478    
479            Iterator<GUIArea> it = contextAreas.iterator();
480    
481            // We now know for sure, that will find the map and the toolbar
482            // since we already called validateCntxtProperties()
483            boolean mapFound = false;
484            boolean toolbarFound = false;
485            while ( it.hasNext() ) {
486                GUIArea area = it.next();
487                boolean found = false;
488    
489                Module[] modules = area.getModules();
490                for ( int i = 0; i < modules.length; i++ ) {
491                    // If the module name was in the list of shownModules then keep it
492                    if ( modulesMap.containsValue( modules[i].getName() ) ) {
493                        found = true;
494                        if ( modules[i].getName().compareTo( modulesMap.get( "MANDATORY_MODULE_MAP" ) ) == 0 ) {
495                            // The map view or the tool bar has been found
496                            modules[i].setWidth( width );
497                            mapFound = true;
498                        }
499    
500                        if ( modules[i].getName().compareTo( modulesMap.get( "MANDATORY_MODULE_TOOLBAR" ) ) == 0 ) {
501                            modules[i].setWidth( width );
502                            toolbarFound = true;
503                        }
504                        continue;
505                    }
506                    modules[i].setHidden( true );
507                }
508                if ( !found ) {
509                    area.setArea( 0 );
510                    area.setHidden( true );
511                }
512            }
513    
514            if ( !mapFound ) {
515                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_MISSING_MODULE",
516                                                                modulesMap.get( "MANDATORY_MODULE_MAP" ) ) );
517            }
518            if ( !toolbarFound ) {
519                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_MISSING_MODULE",
520                                                                modulesMap.get( "MANDATORY_MODULE_TOOLBAR" ) ) );
521            }
522    
523            // Changing the full screen button to normal size screen button
524            Module toolBar = frontEnd.getCenter().getModule( "Toolbar" );
525            if ( toolBar != null ) {
526                String[] removedBtns = ContextMessages.getString( "REMOVED_BUTTONS" ).split( ";" );
527    
528                // Remove only the parameters that exist in the hash map
529                String[] names = toolBar.getParameter().getParameterNames();
530                for ( int i = 0; i < names.length; i++ ) {
531                    for ( int j = 0; j < removedBtns.length; j++ ) {
532                        if ( names[i].indexOf( removedBtns[j] ) >= 0 ) {
533                            toolBar.getParameter().removeParameter( names[i] );
534                        }
535                    }
536                }
537            }
538    
539            Parameter param = new Parameter( ContextMessages.getString( "NORMAL_SIZE_SCREEN" ), "PushButton" );
540            toolBar.addParameter( param );
541    
542            return fullscreenContext;
543        }
544    }