/*******************************************************************************
* Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
* 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
*
* Contributors:
* The Chisel Group, University of Victoria
*******************************************************************************/
package ca.uvic.cs.tagsea.core;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import ca.uvic.cs.tagsea.TagSEAPlugin;
import ca.uvic.cs.tagsea.monitoring.TagSEARoutingEvent;
import ca.uvic.cs.tagsea.monitoring.internal.Monitoring;
import ca.uvic.cs.tagsea.ui.views.RoutesView;
import ca.uvic.cs.tagsea.util.RouteXMLUtil;
/**
* Holds the routes.
*
* @author many
*/
public class RouteCollection implements ITreeContentProvider, ILabelProvider, IColorProvider{
// list of route object's
private List<Route> routes;
private ImageDescriptor routesDescriptor;
private static Image routesImage;
private List <IRouteCollectionListener> fListeners;
private boolean fLoaded = false;
public RouteCollection()
{
this.routes = new ArrayList<Route>();
routesDescriptor = TagSEAPlugin.getImageDescriptor("/icons/route.gif");
}
private Image getRoutesImage() {
if ((routesImage == null) || routesImage.isDisposed()) {
routesImage = routesDescriptor.createImage();
}
return routesImage;
}
/**
* Adds the route if it doesn't already contain it, or one with the same name.
* @param route
*/
public void addRoute(Route route) {
if ((route != null) && !routes.contains(route)) {
for (Route r : routes) {
if (r.equals(route) || r.getName().equals(route.getName())) {
return;
}
}
this.routes.add(route);
Monitoring.getDefault().fireEvent(new TagSEARoutingEvent(TagSEARoutingEvent.Routing.New, route, null));
}
}
public int getSize() {
return routes.size();
}
/**
* Creates and adds a new route if one with the given name
* doesn't already exist.
* @param name
*/
public Route addRoute(String name) {
if (name != null) {
for (Route route : routes) {
if (name.equals(route.getName()))
return route;
}
Route route = new Route(name);
routes.add(route);
Monitoring.getDefault().fireEvent(new TagSEARoutingEvent(TagSEARoutingEvent.Routing.New, route, null));
return route;
}
return null;
}
/**
* Removes the route from the list.
* @param route
*/
public boolean removeRoute(Route route) {
if (route != null) {
Monitoring.getDefault().fireEvent(new TagSEARoutingEvent(TagSEARoutingEvent.Routing.Delete, route, null));
return routes.remove(route);
}
return false;
}
/**
* Returns all the routes.
* @return List of Route objects
*/
public List<Route> getRoutes() {
return routes;
}
/**
* Returns the Route for the given name.
* @param name
* @return Route
*/
public Route getRoute(String name) {
Route route = null;
for (Route r : routes) {
if (r.getName().equals(name)) {
route = r;
break;
}
}
return route;
}
public String toString() {
return "Routes [" + routes.size() + "]";
}
/**
* Gets the root Route objects.
*/
public Object[] getElements(Object parent) {
return getRoutes().toArray();
}
/**
* Returns null.
*/
public Object getParent(Object child) {
return null;
}
/**
* Get children or an empty array if there are no children or specified parent is not a tag.
* @param parent the Route
* @return Object[] the children Tag objects
*/
public Object[] getChildren(Object parent) {
if (parent instanceof Route) {
Route parentTag = (Route) parent;
return parentTag.getWaypoints().toArray();
} else {
return new Object[0];
}
}
/**
* Checks if the given parent (which should be a Tag) has children.
* @param parent the Tag object
* @return boolean if the parent has children
*/
public boolean hasChildren(Object parent) {
if (parent instanceof Route) {
Route route = (Route) parent;
return route.getWaypoints().size() > 0;
} else {
return false;
}
}
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
}
public void dispose() {
}
/**
* Gets the text (the name) for a given Route object.
* @param obj the Tag
*/
public String getText(Object obj)
{
if (obj instanceof Route)
{
Route route = (Route) obj;
return route.getName();
}
else if (obj instanceof Waypoint)
{
Waypoint waypoint = (Waypoint) obj;
return waypoint.getJavaElementName() + " - " + waypoint.getKeyword();
}
return obj.toString();
}
/**
* Gets the image for the given Route object.
* TODO Returns null for now.
*/
public Image getImage(Object element) {
Image img = null;
if (element instanceof Route)
{
img = getRoutesImage();
}
else if (element instanceof Waypoint)
{
Waypoint waypoint = (Waypoint)element;
// trees use different size images than tables!
// we need to set the flag so that the label provider returns a Small (16x16) image
// by default it will return a larger (24x16) image which is used in tables.
// trees need 16x16 images
int flags = JavaElementLabelProvider.SHOW_DEFAULT | JavaElementLabelProvider.SHOW_SMALL_ICONS;
JavaElementLabelProvider jlabel = new JavaElementLabelProvider(flags);
img = jlabel.getImage(waypoint.getJavaElement());
if ((img != null) && img.isDisposed()) {
img = null;
}
}
return img;
}
/** Does nothing. */
public void addListener(ILabelProviderListener listener) {
}
/** Returns false. */
public boolean isLabelProperty(Object element, String property) {
return false;
}
/** Does nothing. */
public void removeListener(ILabelProviderListener listener) {
}
public boolean hasWaypointsInRoutes(){
boolean hasWpsInRoutes = false;
if(getSize()>0){
for (Iterator iter = routes.iterator(); iter.hasNext();) {
Route element = (Route) iter.next();
if(element.getWaypoints().size()>0){
hasWpsInRoutes = true;
break;
}
}
}
return hasWpsInRoutes;
}
/**
* Gets the routes with the given name
* @param ids An array of route names
* @return An array of all tag objects that match the list of route names.
*/
public Route[] getRoutes(String[] ids) {
LinkedList<Route> listOfRoutes = new LinkedList<Route>();
if(ids.length > 0) {
for ( Route route : routes ) {
for(String id : ids) {
if ( route.getName().equals(id) ) {
listOfRoutes.add( route );
}
}
}
}
return listOfRoutes.toArray(new Route[listOfRoutes.size()]);
}
/**
* Updates the routes view.
*/
public void updateView()
{
// @tag reimpliment
// The routes component is badly implemented, it should look for stale waypoints
// by listening to the tag collection, for the moment this hack will work but the
// underlying route and tag storage and managment model needs to be completly
// reimplemented...respect my authoratah!
// Using this implementation the routes may potentially be out of sync
List<Route> routes = getRoutes();
for(Route r : routes)
{
Vector<Waypoint> waypoints = r.getWaypoints();
List<Integer> indices = new ArrayList<Integer>();
int i = 0;
for(Waypoint w : waypoints)
{
if(w.isStale())
{
//@tag badDesign : the waypoint id should be stored in the waypoint
String waypointId = w.getKeyword() + w.getLineNumber();
Waypoint freshCopy = TagSEAPlugin.getDefault().getTagCollection().getWaypointCollection().getWaypoint(waypointId);
if(freshCopy != null)
{
indices.add(i);
}
}
i++;
}
// Cant modify the waypoints while interating under them, thats why this mess exists
if(indices.size() > 0)
for(i = (indices.size() -1); i >= 0; i--)
{
Waypoint w = waypoints.elementAt(indices.get(i));
String waypointId = w.getKeyword() + w.getLineNumber();
waypoints.removeElementAt(indices.get(i));
Waypoint freshCopy = TagSEAPlugin.getDefault().getTagCollection().getWaypointCollection().getWaypoint(waypointId);
waypoints.insertElementAt(freshCopy,indices.get(i));
}
}
RoutesView view = TagSEAPlugin.getDefault().getRoutesView();
if (view != null)
view.getRoutesComposite().refreshRoutesViewer();
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
*/
public Color getBackground(Object element)
{
return Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
*/
public Color getForeground(Object element)
{
if(element instanceof Waypoint)
{
Waypoint w = (Waypoint)element;
if(w.isStale())
return Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
}
return Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
}
public void save()
{
RouteXMLUtil.recordRoutes(this);
}
public void load()
{
RouteXMLUtil.restoreRoutes();
fLoaded = true;
fireLoaded();
}
public synchronized void addRoutesCollectionListener(IRouteCollectionListener listener)
{
getListeners().add(listener);
fireLoaded();
}
private void fireLoaded()
{
if(fLoaded)
Display.getDefault().asyncExec(new Runnable() {
public void run()
{
for(IRouteCollectionListener listener : getListeners())
listener.routesLoaded();
}
});
}
public synchronized void removeRoutesCollectionListener(IRouteCollectionListener listener)
{
getListeners().remove(listener);
}
List <IRouteCollectionListener> getListeners()
{
if(fListeners == null)
fListeners = new ArrayList <IRouteCollectionListener>();
return fListeners;
}
}