001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    /*******************************************************************************
021     * Copyright (c) 2003, 2007 IBM Corporation and others.
022     * All rights reserved. This program and the accompanying materials
023     * are made available under the terms of the Eclipse Public License v1.0
024     * which accompanies this distribution, and is available at
025     * http://www.eclipse.org/legal/epl-v10.html
026     * 
027     * Contributors:
028     *     IBM Corporation - Initial API and implementation
029     *******************************************************************************/
030    package org.apache.directory.studio.apacheds.views;
031    
032    
033    import java.util.ArrayList;
034    import java.util.List;
035    
036    import org.apache.directory.studio.apacheds.model.ServerEvent;
037    import org.apache.directory.studio.apacheds.model.ServerEventEnum;
038    import org.apache.directory.studio.apacheds.model.Server;
039    import org.apache.directory.studio.apacheds.model.ServerListener;
040    import org.apache.directory.studio.apacheds.model.ServerStateEnum;
041    import org.apache.directory.studio.apacheds.model.ServersHandler;
042    import org.apache.directory.studio.apacheds.model.ServersHandlerListener;
043    import org.eclipse.jface.viewers.ISelection;
044    import org.eclipse.jface.viewers.TreeViewer;
045    import org.eclipse.swt.SWT;
046    import org.eclipse.swt.widgets.Display;
047    import org.eclipse.swt.widgets.Tree;
048    import org.eclipse.swt.widgets.TreeColumn;
049    import org.eclipse.swt.widgets.TreeItem;
050    import org.eclipse.swt.widgets.Widget;
051    import org.eclipse.ui.PlatformUI;
052    
053    
054    /**
055     * This class implements a {@link TreeViewer} that displays the servers.
056     *
057     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
058     * @version $Rev$, $Date$
059     */
060    public class ServersTableViewer extends TreeViewer
061    {
062        /** The root element */
063        protected static final String ROOT = "root"; //$NON-NLS-1$
064    
065        /** The label provider */
066        private ServersViewLabelProvider labelProvider;
067    
068        /** The server handler listener */
069        private ServersHandlerListener serversHandlerListener;
070    
071        /** The server listener */
072        private ServerListener serverListener;
073    
074        /** A flag to stop the animation */
075        private boolean stopAnimation;
076    
077        /** The list of server needing animation */
078        private List<Server> serversNeedingAnimation = new ArrayList<Server>();
079    
080    
081        public ServersTableViewer( Tree tree )
082        {
083            super( tree );
084    
085            labelProvider = new ServersViewLabelProvider();
086            setLabelProvider( labelProvider );
087            setContentProvider( new ServersViewContentProvider() );
088    
089            setComparator( new ServersViewerComparator( labelProvider ) );
090    
091            setInput( ROOT );
092    
093            addListeners();
094        }
095    
096    
097        /**
098         * Adds the listener
099         */
100        private void addListeners()
101        {
102            // The server handler listener
103            serversHandlerListener = new ServersHandlerListener()
104            {
105                public void serverAdded( Server server )
106                {
107                    addServer( server );
108                    server.addListener( serverListener );
109                }
110    
111    
112                public void serverRemoved( Server server )
113                {
114                    refreshServer( server );
115                }
116    
117    
118                public void serverUpdated( Server server )
119                {
120                    removeServer( server );
121                    server.removeListener( serverListener );
122    
123                }
124            };
125    
126            // Adding the listener to the servers handler
127            ServersHandler.getDefault().addListener( serversHandlerListener );
128    
129            // The server listener
130            serverListener = new ServerListener()
131            {
132                public void serverChanged( ServerEvent event )
133                {
134                    // Checking if the event is null
135                    if ( event == null )
136                    {
137                        return;
138                    }
139    
140                    // Getting the kind of event and the associated server 
141                    ServerEventEnum kind = event.getKind();
142                    Server server = event.getServer();
143                    switch ( kind )
144                    {
145                        // The server state has changed
146                        case STATE_CHANGED:
147                            // First, we refresh the server
148                            refreshServer( server );
149    
150                            // Then, we get the state of the server to see if we
151                            // need to start or stop the animation thread
152                            ServerStateEnum state = server.getState();
153    
154                            // If the state is STARTING or STOPPING, we need to
155                            // add the server to the list of servers needing
156                            // animation and eventually start the animation thread
157                            if ( ( state == ServerStateEnum.STARTING ) || ( state == ServerStateEnum.STOPPING ) )
158                            {
159                                boolean startAnimationThread = false;
160    
161                                synchronized ( serversNeedingAnimation )
162                                {
163                                    if ( !serversNeedingAnimation.contains( server ) )
164                                    {
165                                        if ( serversNeedingAnimation.isEmpty() )
166                                            startAnimationThread = true;
167                                        serversNeedingAnimation.add( server );
168                                    }
169                                }
170    
171                                if ( startAnimationThread )
172                                {
173                                    startAnimationThread();
174                                }
175                            }
176    
177                            // If the state is *not* STARTING or STOPPING, we need
178                            // to remove the server from the list of servers
179                            // needing animation and eventually stop the animation
180                            // if this list is empty
181                            else
182                            {
183                                boolean stopAnimationThread = false;
184    
185                                synchronized ( serversNeedingAnimation )
186                                {
187                                    if ( serversNeedingAnimation.contains( server ) )
188                                    {
189                                        serversNeedingAnimation.remove( server );
190                                        if ( serversNeedingAnimation.isEmpty() )
191                                            stopAnimationThread = true;
192                                    }
193                                }
194    
195                                if ( stopAnimationThread )
196                                {
197                                    stopAnimationThread();
198                                }
199                            }
200                            break;
201                        // The server has been renamed
202                        case RENAMED:
203                            // We simply refresh the server
204                            refreshServer( server );
205                            break;
206                    }
207    
208                }
209            };
210    
211            // Adding the listener to the servers
212            for ( Server server : ServersHandler.getDefault().getServersList() )
213            {
214                server.addListener( serverListener );
215            }
216        }
217    
218    
219        /**
220         * Adds a server.
221         * 
222         * @param server
223         *      the server
224         */
225        private void addServer( final Server server )
226        {
227            Display.getDefault().asyncExec( new Runnable()
228            {
229                public void run()
230                {
231                    add( ROOT, server );
232                }
233            } );
234        }
235    
236    
237        /**
238         * Refreshes a server.
239         * 
240         * @param server
241         *      the server
242         */
243        private void refreshServer( final Server server )
244        {
245            Display.getDefault().asyncExec( new Runnable()
246            {
247                public void run()
248                {
249                    try
250                    {
251                        refresh( server );
252                        ISelection sel = ServersTableViewer.this.getSelection();
253                        ServersTableViewer.this.setSelection( sel );
254                    }
255                    catch ( Exception e )
256                    {
257                        // ignore
258                    }
259                }
260            } );
261        }
262    
263    
264        /**
265         * Removes a server.
266         * 
267         * @param server
268         *      the server
269         */
270        private void removeServer( final Server server )
271        {
272            Display.getDefault().asyncExec( new Runnable()
273            {
274                public void run()
275                {
276                    remove( server );
277                }
278            } );
279        }
280    
281    
282        /**
283         * Starts the animation thread.
284         */
285        private void startAnimationThread()
286        {
287            stopAnimation = false;
288    
289            final Display display = getTree().getDisplay();
290            final int SLEEP = 200;
291            final Runnable[] animatorThread = new Runnable[1];
292            animatorThread[0] = new Runnable()
293            {
294                public void run()
295                {
296                    // Checking if we need to stop the animation
297                    if ( !stopAnimation )
298                    {
299                        try
300                        {
301                            // Changing the animation state on the label provider
302                            labelProvider.animate();
303    
304                            // Looping on the currently starting servers
305                            for ( Server server : serversNeedingAnimation.toArray( new Server[0] ) )
306                            {
307                                if ( server != null && getTree() != null && !getTree().isDisposed() )
308                                {
309                                    updateAnimation( server );
310                                }
311                            }
312                        }
313                        catch ( Exception e )
314                        {
315                            //                        Trace.trace( Trace.FINEST, "Error in Servers view animation", e ); TODO
316                        }
317    
318                        // Re-launching the animation
319                        display.timerExec( SLEEP, animatorThread[0] );
320                    }
321                }
322            };
323    
324            // Launching the animation asynchronously
325            Display.getDefault().asyncExec( new Runnable()
326            {
327                public void run()
328                {
329                    display.timerExec( SLEEP, animatorThread[0] );
330                }
331            } );
332        }
333    
334    
335        /**
336         * Stops the animation thread.
337         */
338        private void stopAnimationThread()
339        {
340            stopAnimation = true;
341        }
342    
343    
344        /**
345         * Updates the animation for the given server
346         *
347         * @param server
348         *      the server
349         */
350        private void updateAnimation( Server server )
351        {
352            try
353            {
354                Widget widget = doFindItem( server );
355                TreeItem item = ( TreeItem ) widget;
356                item.setText( 1, labelProvider.getColumnText( server, 1 ) );
357                item.setImage( 1, labelProvider.getColumnImage( server, 1 ) );
358            }
359            catch ( Exception e )
360            {
361                //            Trace.trace( Trace.WARNING, "Error in optimized animation", e );
362                //TODO
363            }
364        }
365    
366    
367        /**
368         * Resorts the table based on field.
369         * 
370         * @param column 
371         *      the column being updated
372         * @param col
373         *      the column
374         */
375        protected void resortTable( final TreeColumn column, int col )
376        {
377            ServersViewerComparator sorter = ( ServersViewerComparator ) getComparator();
378    
379            if ( col == sorter.getTopPriority() )
380                sorter.reverseTopPriority();
381            else
382                sorter.setTopPriority( col );
383    
384            PlatformUI.getWorkbench().getDisplay().asyncExec( new Runnable()
385            {
386                public void run()
387                {
388                    refresh();
389                    updateDirectionIndicator( column );
390                }
391            } );
392        }
393    
394    
395        /**
396         * Updates the direction indicator as column is now the primary column.
397         * 
398         * @param column
399         */
400        protected void updateDirectionIndicator( TreeColumn column )
401        {
402            getTree().setSortColumn( column );
403            if ( ( ( ServersViewerComparator ) getComparator() ).getTopPriorityDirection() == ServersViewerComparator.ASCENDING )
404                getTree().setSortDirection( SWT.UP );
405            else
406                getTree().setSortDirection( SWT.DOWN );
407        }
408    }