/*****************************************************************************
* Copyright (c) 2006, 2007 g-Eclipse Consortium
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Initial development of the original code was made for the
* g-Eclipse project founded by European Union
* project number: FP6-IST-034327 http://www.geclipse.eu/
*
* Contributors:
* Mathias Stuempert - initial API and implementation
*****************************************************************************/
package eu.geclipse.ui.providers;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IPath;
import eu.geclipse.core.model.GridModel;
import eu.geclipse.core.model.IGridContainer;
import eu.geclipse.core.model.IGridElement;
/**
* A {@link GridModelContentProvider} that may provide the content
* either in a flat mode or in a hierarchical mode. In flat mode all
* elements will be represented as direct children of the root node.
* In hierarchical mode the elements are represented in their normal
* hierarchical structure as provided by the Grid model.
*/
public class ConfigurableContentProvider
extends GridModelContentProvider {
/**
* Static field that denotes the flat representation mode.
*/
public static final int MODE_FLAT = 1;
/**
* Static field that denotes the hierarchical representation mode.
*/
public static final int MODE_HIERARCHICAL = 2;
/**
* The current mode.
*/
private int mode = MODE_FLAT;
/**
* The root element of the underlying model.
*/
private Object rootElement;
/**
* The list of currently registered listeners.
*/
private List< IConfigurationListener > listeners
= new ArrayList< IConfigurationListener >();
/**
* Add an {@link IConfigurationListener} to the list of currently
* registered listeners.
*
* @param listener The new {@link IConfigurationListener} to be added.
*/
public void addConfigurationListener( final IConfigurationListener listener ) {
this.listeners.add( listener );
}
/* (non-Javadoc)
* @see eu.geclipse.ui.providers.GridModelContentProvider#getChildren(java.lang.Object)
*/
@Override
public Object[] getChildren( final Object parentElement ) {
Object[] children = super.getChildren( parentElement );
if ( this.mode == MODE_HIERARCHICAL ) {
children = filterChildren( children );
}
return children;
}
/* (non-Javadoc)
* @see eu.geclipse.ui.providers.GridModelContentProvider#getElements(java.lang.Object)
*/
@Override
public Object[] getElements( final Object inputElement ) {
this.rootElement = inputElement;
Object[] elements = super.getElements( inputElement );
switch( this.mode ) {
case MODE_HIERARCHICAL:
elements = remapElements( inputElement, elements );
break;
case MODE_FLAT:
elements = filterHierarchicalChildrens( elements );
break;
}
return elements;
}
/**
* Set the current mode of this {@link ConfigurableContentProvider}.
*
* @param m The new mode, i.e. either <code>MODE_FLAT</code>
* or <code>MODE_HIERARCHICAL</code>.
*/
public void setMode( final int m ) {
if ( this.mode != m ) {
this.mode = m;
fireConfigurationChanged();
}
}
/**
* Get the current mode of this {@link ConfigurableContentProvider}.
*
* @return Either <code>MODE_FLAT</code>
* or <code>MODE_HIERARCHICAL</code>.
*/
public int getMode() {
return this.mode;
}
/**
* Remove the specified {@link IConfigurationListener} from the
* list of listeners.
*
* @param listener The listener to be removed.
*/
public void removeConfigurationListener( final IConfigurationListener listener ) {
this.listeners.remove( listener );
}
/**
* Notify all currently registered {@link IConfigurationListener}s
* that this {@link ConfigurableContentProvider} has changed its
* mode.
*/
protected void fireConfigurationChanged() {
for ( IConfigurationListener listener : this.listeners ) {
listener.configurationChanged( this );
}
}
/**
* Determine if the specified element is currently visible.
*
* @param element The element to be tested.
* @return True if the element should be displayed.
*/
protected boolean isVisible( final IGridElement element ) {
boolean result = false;
Object[] rootChildren = super.getChildren( this.rootElement );
IPath elementPath = element.getPath();
for ( Object obj : rootChildren ) {
if ( obj instanceof IGridElement ) {
IPath rootPath = ( ( IGridElement ) obj ).getPath();
if ( elementPath.isPrefixOf( rootPath ) || rootPath.isPrefixOf( elementPath ) ) {
result = true;
break;
}
}
}
return result;
}
/**
* Checks the specified list of children if they are visible
* or not. Creates a new array that only holds the visible
* children.
*
* @param children The children to be tested.
* @return A new array containing all visible children. This
* array may be empty if none of the provided children is
* actually visible.
* @see #isVisible(IGridElement)
*/
private Object[] filterChildren( final Object[] children ) {
List< Object > result = new ArrayList< Object >();
if ( children != null ) {
for ( Object child : children ) {
if ( ( child instanceof IGridElement
&& isVisible( ( IGridElement ) child ) )
|| ( child instanceof ProgressTreeNode ) ) {
result.add( child );
}
}
}
return result.toArray( new Object[ result.size() ] );
}
/**
* Remaps the specified elements with respect to the specified parent.
*
* @param parent The reference object for the remapping.
* @param elements The elements to be remapped.
* @return the remapped elements.
* @see #remapElement(IGridContainer, IGridElement)
*/
private Object[] remapElements( final Object parent,
final Object[] elements ) {
List< Object > result = new ArrayList< Object >();
for ( Object element : elements ) {
if ( ( element instanceof IGridElement ) && ( parent instanceof IGridContainer ) ) {
element = remapElement( ( IGridContainer ) parent,
( IGridElement ) element );
}
if ( !result.contains( element ) ) {
result.add( element );
}
}
return result.toArray( new Object[ result.size() ] );
}
/**
* Remaps the specified element with respect to the specified container.
*
* @param container The reference container for the remapping.
* @param element The element to be remapped.
* @return The element that is a direct or indirect parent of the
* specified element and is a direct child of the specified container.
*/
protected Object remapElement( final IGridContainer container,
final IGridElement element ) {
IGridElement result = element;
IPath parentPath = container.getPath();
IPath childPath = element.getPath();
int childSegments = childPath.segmentCount();
int matchingSegments = childPath.matchingFirstSegments( parentPath );
if ( matchingSegments != childSegments - 1 ) {
IPath remappedPath = childPath.removeLastSegments( childSegments - matchingSegments - 1 );
result = GridModel.getRoot().findElement( remappedPath );
}
return result;
}
/**
* Removes elements, which parent already is on element list (this elements
* will be visibled as children)
*
* @param elements
* @return
*/
private Object[] filterHierarchicalChildrens( final Object[] elements ) {
List< Object > result = new ArrayList< Object >();
for( Object object : elements ) {
if( object instanceof IGridElement ) {
IGridElement gridElement = ( IGridElement )object;
IGridContainer parent = gridElement.getParent();
if( !isOnList( elements, parent ) ) {
result.add( object );
}
} else if ( object instanceof ProgressTreeNode ) {
result.add( object );
}
}
return result.toArray( new Object[ result.size() ] );
}
private boolean isOnList( final Object[] elements, final Object object ) {
boolean onList = false;
for( Object curObject : elements ) {
if( curObject == object ) {
onList = true;
break;
}
}
return onList;
}
}