All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.arakhne.afc.ui.selection.SelectionManager Maven / Gradle / Ivy

There is a newer version: 13.0
Show newest version
/* 
 * $Id: org/arakhne/afc/ui/selection/SelectionManager.java v12.0 2015-04-09 01:26:18$
 * 
 * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
 * Copyright (C) 2012-13 Stephane GALLAND.
 * 
 * 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 3 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
 * This program is free software; you can redistribute it and/or modify
 */
package org.arakhne.afc.ui.selection ;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;

import org.arakhne.afc.util.ListenerCollection;

/** Abstracxt implementation of a selection manager.
 *
 * @param  is the type of the objects inside this manager.
 * @author Stéphane GALLAND
 * @version 12.0 2015-04-09 01:26:18
 * @mavengroupid org.arakhne.afc.ui
 * @mavenartifactid base
 */
public abstract class SelectionManager implements Set {

	private final ListenerCollection listeners = new ListenerCollection();

	private final Class elementType;
	
	/** Create a new SelectionManager.
	 * 
	 * @param elementType is the type of the elements inside this selection manager.
	 */
	public SelectionManager(Class elementType) {
		this.elementType = elementType;
	}
	
	/** Update the system's selection.
	 */
	protected void updateSystemSelection() {
		//
	}

	/** Reset any internal bufferized value.
	 */
	protected void resetInternalBuffers() {
		//
	}

	/** Add the given object inside the inner storage data structure.
	 * 
	 * @param object
	 * @return true if the object was added; false
	 * if not added.
	 */
	protected abstract boolean addInStorage(OBJ object);

	/** Remove the given object from the inner storage data structure.
	 * 
	 * @param object
	 * @return true if the object was removed; false
	 * if not removed.
	 */
	protected abstract boolean removeFromStorage(OBJ object);

	/** Clear the storage
	 * 
	 * @return the removed objects.
	 */
	protected abstract Collection clearStorage();

	/** Replies an iterator on the storage.
	 * 
	 * @return the iterator.
	 */
	protected abstract Iterator getIteratorOnStorage();

	/** Invoked when the given object was removed from the inner storage.
	 * 
	 * @param object
	 */
	protected void onRemovedObject(OBJ object) {
		//
	}

	/** Invoked when the given object was added into the inner storage.
	 * 
	 * @param object
	 */
	protected void onAddedObject(OBJ object) {
		//
	}

	/** Add selection listener.
	 * 
	 * @param listener
	 */
	public final void addSelectionListener(SelectionListener listener) {
		this.listeners.add(SelectionListener.class, listener);
	}

	/** Remove selection listener.
	 * 
	 * @param listener
	 */
	public final void removeSelectionListener(SelectionListener listener) {
		this.listeners.remove(SelectionListener.class, listener);
	}

	/** Notifies the listeners about the selection of a selectable object.
	 * 
	 * @param selectableObject is the selected object.
	 * @param isAdjusting indicates if the event to fire is the last inside
	 * a sequence of events. If true the event to fire must
	 * be followed by other selection events that are produces by the
	 * same action on the selection manager. If false, there
	 * is no following selection event for the same action on the selection
	 * manager.
	 */
	protected final void fireSelected(OBJ selectableObject, boolean isAdjusting) {
		SelectionEvent event = new SelectionEvent(this, selectableObject, false, isAdjusting);
		for(SelectionListener listener : this.listeners.getListeners(SelectionListener.class)) {
			listener.selectionChanged(event);
		}
	}

	/** Notifies the listeners about the deselection of a selectable object.
	 * 
	 * @param selectableObject is the unselected object.
	 * @param isAdjusting indicates if the event to fire is the last inside
	 * a sequence of events. If true the event to fire must
	 * be followed by other selection events that are produces by the
	 * same action on the selection manager. If false, there
	 * is no following selection event for the same action on the selection
	 * manager.
	 */
	protected final void fireUnselected(OBJ selectableObject, boolean isAdjusting) {
		SelectionEvent event = new SelectionEvent(this, selectableObject, true, isAdjusting);
		for(SelectionListener listener : this.listeners.getListeners(SelectionListener.class)) {
			listener.selectionChanged(event);
		}
	}

	/** Toggle the selection of figures.
	 *
	 * @param selectableObject are the figures to toggle.
	 * @return true if the selection of a selectable object
	 * has changed; false if no object has changed
	 * of selection state.
	 */
	public final boolean toggle(OBJ... selectableObject) {
		return toggle(Arrays.asList(selectableObject));
	}
	
	/** Toggle the selection of figures.
	 *
	 * @param selectableObjects are the figures to toggle.
	 * @return true if the selection of a selectable object
	 * has changed; false if no object has changed
	 * of selection state.
	 */
	public synchronized final boolean toggle(Collection selectableObjects) {
		boolean changed = false;
		OBJ selected = null;
		OBJ unselected = null;
		for(OBJ f : selectableObjects) {
			if (removeFromStorage(f)) {
				if (selected!=null) {
					fireSelected(selected, true);
					selected = null;
				}
				else if (unselected!=null) {
					fireUnselected(unselected, true);
					unselected = null;
				}
				onRemovedObject(f);
				changed = true;
				resetInternalBuffers();
				unselected = f;
			}
			else if (f.isSelectable() && addInStorage(f)) {
				if (selected!=null) {
					fireSelected(selected, true);
					selected = null;
				}
				else if (unselected!=null) {
					fireUnselected(unselected, true);
					unselected = null;
				}
				onAddedObject(f);
				changed = true;
				resetInternalBuffers();
				selected = f;
			}
		}
		if (selected!=null) {
			fireSelected(selected, false);
		}
		else if (unselected!=null) {
			fireUnselected(unselected, false);
		}
		if (changed) {
			updateSystemSelection();
		}
		return changed;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized final boolean add(OBJ e) {
		if (e.isSelectable() && addInStorage(e)) {
			onAddedObject(e);
			resetInternalBuffers();
			fireSelected(e, false);
			updateSystemSelection();
			return true;
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized final boolean remove(Object o) {
		if (o!=null && this.elementType.isInstance(o)) {
			OBJ obj = this.elementType.cast(o);
			if (removeFromStorage(obj)) {
				onRemovedObject(obj);
				resetInternalBuffers();
				fireUnselected(obj, false);
				updateSystemSelection();
				return true;
			}
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized final boolean addAll(Collection c) {
		boolean changed = false;
		if (c!=null) {
			OBJ selected = null;
			for(OBJ obj : c) {
				if (obj.isSelectable() && addInStorage(obj)) {
					if (selected!=null) {
						fireSelected(selected, true);
					}
					onAddedObject(obj);
					resetInternalBuffers();
					changed = true;
					selected = obj;
				}
			}
			if (selected!=null) {
				fireSelected(selected, false);
			}
		}
		if (changed) {
			updateSystemSelection();
		}
		return changed;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized final boolean retainAll(Collection c) {
		boolean changed = false;
		if (c==null || c.isEmpty()) {
			changed = !isEmpty();
			clear();
		}
		else {
			OBJ unselected = null;
			Iterator iterator = getIteratorOnStorage();
			OBJ selObject;
			while (iterator.hasNext()) {
				selObject = iterator.next();
				if (!c.contains(selObject)) {
					if (unselected!=null) {
						fireUnselected(unselected, true);
					}
					iterator.remove();
					onRemovedObject(selObject);
					changed = true;
					resetInternalBuffers();
					unselected = selObject;
				}
			}
			if (unselected!=null) {
				fireUnselected(unselected, false);
			}
		}
		if (changed) {
			updateSystemSelection();
		}
		return changed;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized final boolean removeAll(Collection c) {
		boolean changed = false;
		if (c!=null) {
			OBJ unselected = null;
			for(Object o : c) {
				if (o!=null && this.elementType.isInstance(o)) {
					OBJ obj = this.elementType.cast(o);
					if (removeFromStorage(obj)) {
						if (unselected!=null) {
							fireUnselected(unselected, true);
						}
						onRemovedObject(obj);
						resetInternalBuffers();
						changed = true;
						unselected = obj;
					}
				}
			}
			if (unselected!=null) {
				fireUnselected(unselected, false);
			}
		}
		if (changed) {
			updateSystemSelection();
		}
		return changed;
	}

	/**
	 * Select the specified selectable object and deselect all the previously
	 * selected objects.
	 * 
	 * @param e is the selectable object to select.
	 * @return true if the selection has changed;
	 * otherwise false.
	 */
	public final boolean setSelection(OBJ... e) {
		return setSelection(Arrays.asList(e));
	}

	/**
	 * Select the specified selectable object and deselect all the previously
	 * selected objects.
	 * 
	 * @param e is the selectable object to select.
	 * @return true if the selection has changed;
	 * otherwise false.
	 */
	public synchronized final boolean setSelection(Collection e) {
		boolean changed = false;

		Set alreadySelected = new TreeSet();
		OBJ unselected = null;
		OBJ selected = null;

		Iterator iterator = getIteratorOnStorage();
		OBJ selObject;
		while (iterator.hasNext()) {
			selObject = iterator.next();
			if (!e.contains(selObject)) {
				if (unselected!=null) {
					fireUnselected(unselected, true);
				}
				iterator.remove();
				onRemovedObject(selObject);
				changed = true;
				resetInternalBuffers();
				unselected = selObject;
			}
			else {
				alreadySelected.add(selObject);
			}
		}

		for(OBJ fig : e) {
			if (fig.isSelectable() && 
					!alreadySelected.contains(fig)
					&& addInStorage(fig)) {
				if (selected!=null) {
					fireSelected(selected, true);
				}
				else if (unselected!=null) {
					fireUnselected(unselected, true);
					unselected = null;
				}
				onAddedObject(fig);
				changed = true;
				resetInternalBuffers();
				selected = fig;
			}
		}
		
		if (selected!=null) {
			fireSelected(selected, false);
		}
		else if (unselected!=null) {
			fireUnselected(unselected, false);
		}

		if (changed) {
			updateSystemSelection();
		}

		return changed;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized final Iterator iterator() {
		return new SelectionIterator(getIteratorOnStorage());
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized final void clear() {
		Collection elements = clearStorage();
		if (!elements.isEmpty()) {
			resetInternalBuffers();
			updateSystemSelection();
			Iterator iterator = elements.iterator();
			OBJ obj;
			while (iterator.hasNext()) {
				obj = iterator.next();
				onRemovedObject(obj);
				resetInternalBuffers();
				fireUnselected(obj, iterator.hasNext());
			}
		}
	}

	/** 
	 * @author Stéphane GALLAND
	 * @version 12.0 2015-04-09 01:26:18
	 * @mavengroupid org.arakhne.afc.ui
	 * @mavenartifactid base
	 */
	private class SelectionIterator implements Iterator {

		private final Iterator iterator;

		private OBJ lastObject = null;

		public SelectionIterator(Iterator iterator) {
			this.iterator = iterator;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public boolean hasNext() {
			synchronized(SelectionManager.this) {
				return this.iterator.hasNext();
			}
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public OBJ next() {
			synchronized(SelectionManager.this) {
				this.lastObject = this.iterator.next();
			}
			return this.lastObject;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void remove() {
			OBJ obj = this.lastObject;
			this.lastObject = null;
			if (obj==null) throw new NoSuchElementException();
			synchronized(SelectionManager.this) {
				this.iterator.remove();
			}
			onRemovedObject(obj);
			resetInternalBuffers();
			fireUnselected(obj, false);
			updateSystemSelection();
		}

	} // class SelectionIterator

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy