//$HeadURL$ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2012 by: Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: lat/lon GmbH Aennchenstr. 19 53177 Bonn Germany E-Mail: info@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.igeo; import java.awt.Container; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.swing.AbstractButton; import javax.swing.JInternalFrame; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.util.StringTools; import org.deegree.igeo.config.MenuBarType; import org.deegree.igeo.config.ModuleGroupType; import org.deegree.igeo.config.ModuleRegisterType; import org.deegree.igeo.config.ModuleType; import org.deegree.igeo.config.PopUpEntryType; import org.deegree.igeo.config.Project; import org.deegree.igeo.config.SettingsType; import org.deegree.igeo.config.ToolbarEntryType; import org.deegree.igeo.config.Util; import org.deegree.igeo.config._ComponentPositionType; import org.deegree.igeo.i18n.Messages; import org.deegree.igeo.io.FileSystemAccess; import org.deegree.igeo.io.FileSystemAccessFactory; import org.deegree.igeo.mapmodel.MapModel; import org.deegree.igeo.mapmodel.MapModelCollection; import org.deegree.igeo.modules.DefaultMapModule; import org.deegree.igeo.modules.IModule; import org.deegree.igeo.modules.IModuleGroup; import org.deegree.igeo.modules.ModuleCreator; import org.deegree.igeo.modules.ModuleException; import org.deegree.igeo.settings.Settings; import org.deegree.igeo.views.IFooter; import org.deegree.igeo.views.swing.ButtonGroup; import org.deegree.igeo.views.swing.ControlElement; import org.deegree.igeo.views.swing.MenuBar; import org.deegree.igeo.views.swing.util.GenericFileChooser.FILECHOOSERTYPE; import org.deegree.kernel.CommandProcessor; import org.deegree.kernel.ProcessMonitor; import org.deegree.kernel.ProcessMonitorFactory; import org.deegree.model.Identifier; /** * TODO add class documentation * * @author <a href="mailto:wanhoff@lat-lon.de">Jeronimo Wanhoff</a> * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author last edited by: $Author$ * * @version. $Revision$, $Date$ */ public abstract class ApplicationContainer<T> { private static final ILogger LOG = LoggerFactory.getLogger( ApplicationContainer.class ); public static final String ASSIGNEDMAPMODEL = "assignedMapModel"; private ModuleCreator<T> moduleCreator; protected Project proj = null; protected List<IModule<T>> modules = Collections.synchronizedList( new ArrayList<IModule<T>>( 50 ) ); protected List<ToolbarEntryType> toolbarEntries; protected List<PopUpEntryType> popupEntries; protected ProcessMonitor processMonitor; protected IFooter footer; protected boolean isNew; protected MapModelCollection mapModelCollection; protected URL projectURL; protected MapModel activeMapModel; protected CommandProcessor commandProcessor; protected Settings settings; protected Map<String, String> certificates = new HashMap<String, String>(); protected String user = null; protected String password = ""; protected Map<String, ButtonGroup> btGroups = new LinkedHashMap<String, ButtonGroup>( 10 ); protected Map<String, Object> instanceSettings = new HashMap<String, Object>(); protected JPanel toolbarPanel; protected MenuBar menuBar; protected JPopupMenu popup; protected ControlElement menuBarController; protected ControlElement toolBarController; protected Map<Identifier, Map<String, List<Object>>> actionMap = new LinkedHashMap<Identifier, Map<String, List<Object>>>(); protected Map<String, List<AbstractButton>> toolbarButtons; protected Map<String, AbstractButton> menuItems; protected Container rootTargetPane = null; /** * * @param processMonitor */ protected ApplicationContainer( ProcessMonitor processMonitor ) { if ( processMonitor == null ) { processMonitor = ProcessMonitorFactory.createConsoleProcessMonitor( "", "", 0, 100, null ); } this.processMonitor = processMonitor; commandProcessor = new CommandProcessor(); } /** * * @return {@link CommandProcessor} assigend to a project */ public CommandProcessor getCommandProcessor() { return commandProcessor; } /** * * @return all {@link ButtonGroup}s used for menus, toolbars and popups */ public Map<String, ButtonGroup> getButtonGroups() { return btGroups; } /** * loads a project and initializes the implementing application container * * @param url * the url where the description of the project can be found * @param isNew * must be true if the loaded project should be treated as a new project (not already been saved) * @throws JAXBException * @throws IOException * @throws URISyntaxException */ public void loadProject( URL url, boolean isNew ) throws JAXBException, IOException, URISyntaxException { this.isNew = isNew; this.projectURL = url; // kill al existing modules and their GUI classes for ( IModule<?> module : modules ) { Object obj = module.getGUIContainer(); if ( obj instanceof Window ) { ( (Window) obj ).dispose(); } else if ( obj instanceof JInternalFrame ) { ( (JInternalFrame) obj ).dispose(); } module.clear(); } modules.clear(); toolbarEntries = new ArrayList<ToolbarEntryType>(); popupEntries = new ArrayList<PopUpEntryType>(); processMonitor.updateStatus( "loading project file" ); JAXBContext jc = JAXBContext.newInstance( "org.deegree.igeo.config" ); Unmarshaller u = jc.createUnmarshaller(); this.proj = (Project) u.unmarshal( url ); // load admin settings if available String as = System.getProperty( "adminSettings" ); SettingsType asst = null; if ( as != null ) { JAXBElement<?> ele = (JAXBElement<?>) u.unmarshal( new URL( as ) ); asst = (SettingsType) ele.getValue(); } // load user settings if available String us = System.getProperty( "userSettings" ); SettingsType usst = null; if ( us != null ) { JAXBElement<?> ele = (JAXBElement<?>) u.unmarshal( new URL( us ) ); usst = (SettingsType) ele.getValue(); } settings = new Settings( this, asst, usst, proj.getSettings() ); try { moduleCreator = new ModuleCreator<T>( this, url ); } catch ( Exception e ) { // can never happen because project configuration has already been loaded before e.printStackTrace(); } processMonitor.updateStatus( "creating menus ..." ); String lang = this.proj.getLanguage(); if ( lang == null ) { lang = Locale.getDefault().getLanguage(); } Locale.setDefault( new Locale( lang ) ); proj.setLanguage( lang ); // add toolbar entries of the main view into map if ( proj.getView().getToolBar().size() > 0 ) { List<ToolbarEntryType> tbe = proj.getView().getToolBar().get( 0 ).getToolBarEntry(); for ( ToolbarEntryType entry : tbe ) { addToolBarEntryToList( entry ); } } if ( proj.getView().getPopUpMenu() != null ) { List<PopUpEntryType> pme = proj.getView().getPopUpMenu().getPopUpEntry(); for ( PopUpEntryType entry : pme ) { popupEntries.add( entry ); } } processMonitor.updateStatus( "loading map model" ); mapModelCollection = moduleCreator.createMapModelCollection( this.proj.getMapModelCollection(), processMonitor ); // set active/current map model. If no map model in configuration is marked as current // first available map model will used as active/current. this.activeMapModel = null; List<MapModel> list = mapModelCollection.getMapModels(); for ( MapModel mapModel : list ) { if ( mapModel.isCurrent() ) { this.activeMapModel = mapModel; break; } } if ( this.activeMapModel == null ) { this.activeMapModel = mapModelCollection.getMapModels().get( 0 ); this.activeMapModel.setCurrent( true ); } // initialize module List<ModuleRegisterType> moduleRegister = this.proj.getView().getModuleRegister(); for ( ModuleRegisterType mr : moduleRegister ) { JAXBElement<? extends ModuleType> moduleType = mr.getModule(); _ComponentPositionType componentPosition = mr.get_ComponentPosition().getValue(); IModule<T> module = null; if ( moduleType != null ) { ModuleType mt = moduleType.getValue(); if ( mt instanceof ModuleGroupType ) { ModuleGroupType mg = (ModuleGroupType) mt; processMonitor.updateStatus( "loading module: " + mg.getName() ); module = moduleCreator.createModuleGroup( mg, componentPosition, null, processMonitor ); } else { processMonitor.updateStatus( "loading module: " + mt.getName() ); module = moduleCreator.createModule( mt, componentPosition, null, processMonitor ); } } else { URL mLoc = null; String href = mr.getModuleReference().getOnlineResource().getHref(); try { FileSystemAccessFactory fsaf = FileSystemAccessFactory.getInstance( this ); FileSystemAccess fsa = fsaf.getFileSystemAccess( FILECHOOSERTYPE.module ); mLoc = fsa.getFileURL( href ); } catch ( Exception e ) { LOG.logError( e.getMessage(), e ); String msg = Messages.getMessage( Locale.getDefault(), "$DG10043", href ); throw new ModuleException( msg, e ); } JAXBContext jc2 = JAXBContext.newInstance( "org.deegree.igeo.config" ); Unmarshaller u2 = jc2.createUnmarshaller(); LOG.logDebug( "loading: ", mLoc ); try { @SuppressWarnings("unchecked") ModuleType mt = ( (JAXBElement<? extends ModuleType>) u2.unmarshal( mLoc ) ).getValue(); processMonitor.updateStatus( "loading module: " + mt.getName() ); module = moduleCreator.createModule( mt, componentPosition, null, processMonitor ); } catch ( Exception e ) { throw new IOException( "can not read module from: " + mLoc + " \n\nstacktrace: \n" + StringTools.stackTraceToString( e ) ); } } this.modules.add( module ); } assigneMapModel( this.modules ); } /** * ensure that a map module (if not already assigned) is assigned to map model * * @param modules */ private void assigneMapModel( List<IModule<T>> modules ) { for ( IModule<T> module : modules ) { if ( module instanceof IModuleGroup<?> ) { List<IModule<T>> tmp = ( (IModuleGroup<T>) module ).getChildModules(); assigneMapModel( tmp ); } else if ( "MapModule".equals( module.getName() ) && module.getInitParameter( ASSIGNEDMAPMODEL ) == null ) { module.setInitParameter( ASSIGNEDMAPMODEL, activeMapModel.getIdentifier().getAsQualifiedString() ); } } } /** * * @return projects settings */ public Settings getSettings() { return settings; } /** * * @return projects menu bar definition */ public MenuBarType getMenuBar() { return proj.getView().getMenuBar(); } /** * * @param url * @return * @throws MalformedURLException */ public URL resolve( String url ) throws MalformedURLException { return moduleCreator.resolve( url ); } /** * removes a module from a project </br> an implementing class may overrides this method to remove all according * menu bar, tool bar and pop up entries * * @param module */ public void removeModule( IModule<T> module ) { this.modules.remove( module ); List<ModuleRegisterType> moduleRegister = this.proj.getView().getModuleRegister(); deleteModuleRegister( moduleRegister, module.getIdentifier() ); } private void deleteModuleRegister( List<ModuleRegisterType> moduleRegister, Identifier id ) { ModuleRegisterType tmp = null; for ( ModuleRegisterType mr : moduleRegister ) { JAXBElement<? extends ModuleType> moduleType = mr.getModule(); if ( moduleType != null ) { ModuleType mt = moduleType.getValue(); if ( mt instanceof ModuleGroupType ) { ModuleGroupType mg = (ModuleGroupType) mt; List<ModuleRegisterType> mrt = mg.getModuleRegister(); // recursion deleteModuleRegister( mrt, id ); } else { if ( Util.convertIdentifier( mt.getIdentifier() ).equals( id ) ) { tmp = mr; break; } } } } moduleRegister.remove( tmp ); } /** * * @param identifier * @return Module identified by passed {@link Identifier} or <code>null</code> if no matching module can be found */ public IModule<T> findModuleByIdentifier( Identifier identifier ) { for ( int i = 0; i < this.modules.size(); i++ ) { if ( this.modules.get( i ).getIdentifier().equals( identifier ) ) { return this.modules.get( i ); } if ( this.modules.get( i ) instanceof IModuleGroup<?> ) { IModule<T> mod = searchChildren( (IModuleGroup<T>) this.modules.get( i ), identifier ); if ( mod != null ) { return mod; } } } return null; } /** * * @param moduleGroup * @param identifier * @return module matching passed identifier */ private IModule<T> searchChildren( IModuleGroup<T> moduleGroup, Identifier identifier ) { List<IModule<T>> list = moduleGroup.getChildModules(); for ( IModule<T> module : list ) { if ( module.getIdentifier().equals( identifier ) ) { return module; } if ( module instanceof IModuleGroup<?> ) { return searchChildren( (IModuleGroup<T>) module, identifier ); } } return null; } /** * * @param name * @return Module identified by passed name or <code>null</code> if no matching module can be found */ public List<IModule<T>> findModuleByName( String name ) { List<IModule<T>> result = new ArrayList<IModule<T>>(); for ( int i = 0; i < this.modules.size(); i++ ) { if ( this.modules.get( i ).getName().equals( name ) ) { result.add( this.modules.get( i ) ); } else { if ( this.modules.get( i ) instanceof IModuleGroup<?> ) { searchChildren( (IModuleGroup<T>) this.modules.get( i ), name, result ); } } } return result; } /** * * @param moduleGroup * @param name * @param result */ private void searchChildren( IModuleGroup<T> moduleGroup, String name, List<IModule<T>> result ) { List<IModule<T>> list = moduleGroup.getChildModules(); for ( IModule<T> module : list ) { if ( module.getName().equals( name ) ) { result.add( module ); } else { if ( module instanceof IModuleGroup<?> ) { searchChildren( (IModuleGroup<T>) module, name, result ); } } } } /** * * @return list of all registered modules */ public List<IModule<T>> getModules() { return this.modules; } /** * Fetches and returns the active map module * * @return the active map module */ public DefaultMapModule<T> getActiveMapModule() { MapModel mm = getMapModel( null ); List<IModule<T>> list = findModuleByName( "MapModule" ); for ( IModule<T> module : list ) { String amm = module.getInitParameter( "assignedMapModel" ); if ( mm.getIdentifier().getValue().equals( amm ) ) { return (DefaultMapModule<T>) module; } } if ( list.size() == 0 ) { return null; } return (DefaultMapModule<T>) list.get( 0 ); } /** * adds a toolbar entry into list of defined entries * * @param entry */ public void addToolBarEntryToList( ToolbarEntryType entry ) { toolbarEntries.add( entry ); } /** * * @return defined popup entries */ public List<PopUpEntryType> getPopupEntries() { return popupEntries; } /** * * @return GUI independent footer */ public IFooter getFooter() { return footer; } /** * * @return assigned {@link MapModelCollection} */ public MapModelCollection getMapModelCollection() { return mapModelCollection; } /** * * @param id * @return map model matching passed id */ public MapModel getMapModel( Identifier id ) { if ( id != null ) { List<MapModel> mapModels = mapModelCollection.getMapModels(); for ( MapModel mapModel : mapModels ) { if ( mapModel.getIdentifier().equals( id ) ) { return mapModel; } } } return activeMapModel; } /** * sets active {@link MapModel} * * @param mapModel */ public void setActiveMapModel( MapModel mapModel ) { List<MapModel> list = mapModelCollection.getMapModels(); for ( MapModel mapModel2 : list ) { mapModel2.setCurrent( false ); } if ( !this.activeMapModel.equals( mapModel ) ) { mapModel.setCurrent( true ); // just fire action event if really something has changed this.activeMapModel = mapModel; ActionEvent e = new ActiveMapModelChanged( this, "ActiveMapModelChanged", mapModel ); for ( IModule<T> module : modules ) { module.actionPerformed( e ); if ( module instanceof IModuleGroup<?> ) { invokeChildren( (IModuleGroup<T>) module, e ); } } } } /** * * @param moduleGroup * @param e */ private void invokeChildren( IModuleGroup<T> moduleGroup, ActionEvent e ) { List<IModule<T>> list = moduleGroup.getChildModules(); for ( IModule<T> module : list ) { module.actionPerformed( e ); if ( module instanceof IModuleGroup<?> ) { invokeChildren( (IModuleGroup<T>) module, e ); } } } /** * * @param url * @return certificate for service that is identified by its URL */ public String getCertificate( String url ) { String certificate = certificates.get( url ); if ( certificate == null ) { certificate = certificates.get( "default" ); } return certificate; } /** * * @return name of the current user */ public String getUser() { return user; } /** * * @return password of the current user */ public String getPassword() { return password; } /** * * @param name * @return parameter that is global available for complete time an instance of iGeoDesktop is running or it has been * deleted manually */ public synchronized Object getInstanceSetting( String name ) { return instanceSettings.get( name ); } /** * sets a parameter that will be global available for complete time an instance of iGeoDesktop is running or it has * been deleted manually but it will not be stored * * @param name * @param value */ public synchronized void setInstanceSetting( String name, Object value ) { instanceSettings.put( name, value ); } /** * * @return project language */ public String getLanguage() { return proj.getLanguage(); } /** * * @return url/location of the loaded project file */ public String getProjectURL() { try { return projectURL.toURI().toASCIIString(); } catch ( URISyntaxException e ) { e.printStackTrace(); } return projectURL.toExternalForm(); } /** * * @param url */ public void setProjectURL( URL url ) { this.projectURL = url; } /** * * @param language * project language */ public void setLanguage( String language ) { proj.setLanguage( language ); } /** * @return the proj */ public Project getProject() { return proj; } /** * @return the processMonitor */ public ProcessMonitor getProcessMonitor() { return processMonitor; } /** * logs out current instance of igeodesktop by reseting all certificates */ public void logout() { user = null; password = null; certificates.clear(); } /** * @return the isNew */ public boolean isNew() { return isNew; } /** * @param isNew * the isNew to set */ public void setNew( boolean isNew ) { this.isNew = isNew; } /** * @param processMonitor * the processMonitor to set */ public void setProcessMonitor( ProcessMonitor processMonitor ) { this.processMonitor = processMonitor; } /** * * @return pane to which GUI container of all root level modules will be added */ public Container getRootTargetPane() { return rootTargetPane; } /** * The key of the map contains the name of the listener class separated by an '|' from the name of the menu item: * <p> * <code>listener.getClass().getName() + "|" + mt.getName()</code> * </p> * * @return all known menu items */ public Map<String, AbstractButton> getMenuItems() { return menuItems; } /** * initializes iGeoDesktop */ public abstract void init(); /** * This method is defined abstract because it depends on concrete implementations how to access an applications * locale. E.g. a swing based desktop application may simply returns <code>Locale.getDefault()</code> while a web * based application evaluates the locale of the incomming requests. * * @return local to be used with a application container. */ public abstract Locale getLocale(); /** * * @param module * identifier of module an action is assigned to * @param action * name of the action * @param select */ public abstract void selectComponentForAction( Identifier module, String action, boolean select ); /** * registers a {@link KeyStroke} for an Action to be assigned to the root window * * @param actionListener * @param keyStroke */ public abstract void registerKeyboardAction( ActionListener actionListener, KeyStroke keyStroke ); /** * */ public abstract void resetToolbar(); /** * @return the platform of the application */ public abstract String getViewPlatform(); /** * frees all resources allocated by an instance of ApplicationContainer */ public abstract void cleanUp(); /** * * @return main window of the application */ public abstract Container getMainWndow(); /** * performs a login * * @param user * @param password */ public abstract void login( String user, String password ); /** * renders the application */ public abstract void paint(); /** * adapts the toolbar to current container size */ public abstract void resizeToolbar(); /** * add entries (buttons) to the toolbar assigned to one specific module and its children * * @param module */ public abstract void addToolBarEntries( IModule<Container> module ); /** * appends Modules onto the passed contains * * @param targetPane */ public abstract void appendModules( List<IModule<Container>> modules, Container targetPane ); }