// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/omGraphics/OMGraphicList.java,v $
// $RCSfile: OMGraphicList.java,v $
// $Revision: 1.22 $
// $Date: 2009/02/20 17:15:27 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.omGraphics;
import java.awt.Paint;
import java.awt.TexturePaint;
import java.io.EOFException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import com.bbn.openmap.omGraphics.grid.OMGridGenerator;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;
/**
* This class encapsulates a List of OMGraphics.
* <p>
* There are several things that this list does that make it better that any ol'
* List. You can make several common OMGraphic modification calls on the list,
* and the list handles the iteration and changing of all the graphics while
* taking into account a travese order.
* <p>
* An additional benefit is that because the OMGraphicList extends OMGraphic it
* can contain other instances of OMGraphicList. This way you can manage
* groupings of graphics, (for instance, an OMGraphicList of OMGraphicLists
* which each have an OMRaster and OMText).
* <p>
* Many methods, such as generate() and findClosest() traverse the items in the
* GraphicsList recursively. The direction that the list is traversed is
* controlled by then traverseMode variable. The traverseMode mode lets you set
* whether the first or last object added to the list (FIRST_ADDED_ON_TOP or
* LAST_ADDED_ON_TOP) is drawn on top of the list and considered first for
* searches.
*/
public class OMGraphicList extends OMList<OMGraphic> implements Serializable {
/**
* Construct an OMGraphicList.
*/
public OMGraphicList() {
this(10);
}
/**
* Construct an OMGraphicList with an initial capacity.
*
* @param initialCapacity the initial capacity of the list
*/
public OMGraphicList(int initialCapacity) {
graphics = Collections.synchronizedList(new ArrayList<OMGraphic>(initialCapacity));
}
/**
* Construct an OMGraphicList to include a Collection of OMGraphics.
*
* @param c Collection of OMGraphics.
*/
public OMGraphicList(Collection<OMGraphic> c) {
graphics.addAll(c);
}
/**
* Returns a iterator of a shallow copy of the current list, to avoid
* concurrent modification exceptions if the list is being generated or
* rendered while the list is being reviewed for other reasons.
*/
public Iterator<OMGraphic> iteratorCopy() {
return new OMGraphicList(graphics).iterator();
}
/**
* Returns a iterator of a shallow copy of the current list, to avoid
* concurrent modification exceptions if the list is being generated or
* rendered while the list is being reviewed for other reasons.
*/
public ListIterator<OMGraphic> listIteratorCopy() {
return new OMGraphicList(graphics).listIterator();
}
/**
* Returns a iterator of a shallow copy of the current list, to avoid
* concurrent modification exceptions if the list is being generated or
* rendered while the list is being reviewed for other reasons.
*/
public ListIterator<OMGraphic> listIteratorCopy(int size) {
return new OMGraphicList(graphics).listIterator(size);
}
/**
* Add an OMGraphic to the list.
*/
public boolean add(OMGraphic g) {
checkForDuplicate(g);
return graphics.add(g);
}
/**
* Set the graphic at the specified location. The OMGraphic must not be
* null.
*
* @param graphic OMGraphic
* @param index index of the OMGraphic to return
* @exception ArrayIndexOutOfBoundsException if index is out-of-bounds
*/
public void setOMGraphicAt(OMGraphic graphic, int index) {
graphics.set(index, graphic);
}
public OMGraphic getOMGraphicAt(int index) {
return get(index);
}
/**
* Get the geometry at the location number on the list.
*
* @param location the location of the OMGraphic to return
* @return OMGraphic or null if location > list size
* @exception ArrayIndexOutOfBoundsException if <code>location < 0</code>
* or <code>location >=
* this.size()</code>
*/
public OMGraphic get(int location) {
return graphics.get(location);
}
/**
* Set the stroke of all the graphics on the list.
*
* @param stroke the stroke object to use.
*/
public void setStroke(java.awt.Stroke stroke) {
super.setStroke(stroke);
synchronized (graphics) {
for (OMGraphic omg : graphics) {
omg.setStroke(stroke);
}
}
}
/**
* Set the fill paint for all the objects on the list.
*
* @param paint java.awt.Paint
*/
public void setFillPaint(Paint paint) {
super.setFillPaint(paint);
synchronized (graphics) {
for (OMGraphic omg : graphics) {
omg.setFillPaint(paint);
}
}
}
/**
* Set the texture mask for the OMGraphics on the list. If not null, then it
* will be rendered on top of the fill paint. If the fill paint is clear,
* the texture mask will not be used. If you just want to render the texture
* mask as is, set the fill paint of the graphic instead. This is really to
* be used to have a texture added to the graphic, with the fill paint still
* influencing appearance.
*/
public void setTextureMask(TexturePaint texture) {
super.setTextureMask(texture);
synchronized (graphics) {
for (OMGraphic omg : graphics) {
omg.setTextureMask(texture);
}
}
}
/**
* Set the line paint for all the objects on the list.
*
* @param paint java.awt.Paint
*/
public void setLinePaint(Paint paint) {
super.setLinePaint(paint);
synchronized (graphics) {
for (OMGraphic omg : graphics) {
omg.setLinePaint(paint);
}
}
}
/**
* Set the selection paint for all the objects on the list.
*
* @param paint java.awt.Paint
*/
public void setSelectPaint(Paint paint) {
super.setSelectPaint(paint);
synchronized (graphics) {
for (OMGraphic omg : graphics) {
omg.setSelectPaint(paint);
}
}
}
/**
* Set the matting paint for all the objects on the list.
*
* @param paint java.awt.Paint
*/
public void setMattingPaint(Paint paint) {
super.setMattingPaint(paint);
synchronized (graphics) {
for (OMGraphic omg : graphics) {
omg.setMattingPaint(paint);
}
}
}
/**
* Set the matting flag for all the objects on the list.
*/
public void setMatted(boolean value) {
super.setMatted(value);
synchronized (graphics) {
for (OMGraphic omg : graphics) {
omg.setMatted(value);
}
}
}
/**
* Goes through the list, finds the OMGrid objects, and sets the generator
* for all of them. If a projection is passed in, the generator will be used
* to create a displayable graphic within the grid.
*
* @param generator an OMGridGenerator to create a renderable graphic from
* the OMGrid.
* @param proj a projection to use to generate the graphic. If null, the
* generator will create a renderable graphic the next time a
* projection is handed to the list.
*/
public void setGridGenerator(OMGridGenerator generator,
Projection proj) {
synchronized (graphics) {
for (OMGraphic graphic : graphics) {
if (graphic instanceof OMGrid) {
((OMGrid) graphic).setGenerator(generator);
if (proj != null) {
graphic.generate(proj);
}
}
}
}
}
/**
* Get a reference to the graphics vector. This method is meant for use by
* methods that need to iterate over the graphics vector, or make at least
* two invocations on the graphics vector.
* <p>
* HACK this method should either return a clone of the graphics list or a
* quick reference. Currently it returns the latter for simplicity and minor
* speed improvement. We should allow a way for the user to set the desired
* behavior, depending on whether they want responsibility for list
* synchronization. Right now, the user is responsible for synchronizing the
* OMGraphicList if it's being used in two or more threads...
*
* @return a reference of the graphics List.
*/
public List<OMGraphic> getTargets() {
if (graphics == null) {
// make sure that the graphics vector is not null,
// since all of the internal methods rely on it.
graphics = Collections.synchronizedList(new ArrayList<OMGraphic>(10));
}
return graphics;
}
/**
* Set the List used to hold the OMGraphics. The OMGraphicList assumes that
* this list contains OMGraphics. Make *SURE* this is the case. The
* OMGraphicList will behave badly if there are non-OMGraphics on the list.
*/
public void setTargets(List<OMGraphic> list) {
graphics = Collections.synchronizedList(new ArrayList<OMGraphic>(list));
}
/**
* Read a cache of OMGraphics, given an URL.
*
* @param cacheURL URL of serialized graphic list.
*/
public void readGraphics(URL cacheURL) throws IOException {
try {
ObjectInputStream objstream = new ObjectInputStream(cacheURL.openStream());
if (Debug.debugging("omgraphics")) {
Debug.output("OMGraphicList: Opened " + cacheURL.toString());
}
readGraphics(objstream);
objstream.close();
if (Debug.debugging("omgraphics")) {
Debug.output("OMGraphicList: closed " + cacheURL.toString());
}
} catch (ArrayIndexOutOfBoundsException aioobe) {
throw new com.bbn.openmap.util.HandleError(aioobe);
} catch (ClassCastException cce) {
cce.printStackTrace();
}
}
/**
* Read a cache of OMGraphics, given a ObjectInputStream.
*
* @param objstream ObjectInputStream of graphic list.
*/
public void readGraphics(ObjectInputStream objstream)
throws IOException {
Debug.message("omgraphics", "OMGraphicList: Reading cached graphics");
try {
while (true) {
try {
OMGraphic omg = (OMGraphic) objstream.readObject();
graphics.add(omg);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (OptionalDataException ode) {
ode.printStackTrace();
}
}
} catch (EOFException e) {
}
}
/**
* Write the graphics out to a file
*
* @param graphicsSaveFile
*/
public void writeGraphics(String graphicsSaveFile) throws IOException {
FileOutputStream ostream = new FileOutputStream(graphicsSaveFile);
ObjectOutputStream objectstream = new ObjectOutputStream(ostream);
writeGraphics(objectstream);
objectstream.close();
}
/**
* Write the graphics out to a ObjectOutputStream
*
* @param objectstream ObjectOutputStream
*/
public void writeGraphics(ObjectOutputStream objectstream)
throws IOException {
synchronized (graphics) {
for (Iterator<OMGraphic> it = iterator(); it.hasNext();) {
OMGraphic g = it.next();
try {
objectstream.writeObject(g);
} catch (IOException e) {
Debug.error("OMGraphicList: Couldn't write object " + g
+ "\nOMGraphicList: Reason: " + e.toString());
}
}
}
objectstream.close();
}
/**
* This sort method is a place-holder for OMGraphicList extensions to
* implement their own particular criteria for sorting an OMGraphicList.
* Does nothing for a generic OMGraphicList.
*/
public void sort() {}
/**
* Convenience method to cast an object to an OMGraphic if it is one.
* Returns null if it isn't.
*/
protected OMGraphic objectToOMGraphic(Object obj) {
if (obj instanceof OMGraphic) {
return (OMGraphic) obj;
} else {
return null;
}
}
/**
* @return a duplicate list full of shallow copies of each of the OMGraphics
* contained on the list.
*/
public Object clone() {
OMGraphicList omgl = (OMGraphicList) super.clone();
synchronized(graphics){
for (OMGraphic omg : graphics) {
// If the OMGraphic doesn't provide a copy (providing a
// SinkGraphic instead), oh well.
if (omg instanceof OMGraphicList) {
omgl.add((OMGraphic) ((OMGraphicList) omg).clone());
} else {
omgl.graphics.add(omg);
}
}
}
return omgl;
}
@Override
public OMList<OMGraphic> create() {
return new OMGraphicList();
}
@Override
protected com.bbn.openmap.omGraphics.OMList.OMDist<OMGraphic> createDist() {
return new OMDist<OMGraphic>();
}
public void add(int index, OMGraphic element) {
setNeedToRegenerate(true);
super.add(index, element);
}
public boolean addAll(Collection<? extends OMGraphic> c) {
setNeedToRegenerate(true);
return graphics.addAll(c);
}
public boolean addAll(int index, Collection<? extends OMGraphic> c) {
setNeedToRegenerate(true);
return graphics.addAll(index, c);
}
public OMGraphic set(int index, OMGraphic element) {
setNeedToRegenerate(true);
return graphics.set(index, element);
}
/**
* Remove the geometry at the location number.
*
* @param location the location of the OMGeometry to remove
*/
public OMGraphic remove(int location) {
OMGraphic obj = super.remove(location);
if (obj != null) {
setNeedToRegenerate(true);
}
return obj;
}
}