Issue #1638 Fixed localization feature dependencies. Updated plugin data object resource to be thread safe. Moved common legend operations to DefaultLegendResource.

Amend: Corrected ticket number

Change-Id: Ice336b6b4fb286dd4905f43b6f16259f23dea0c6

Former-commit-id: 67e0d17aed [formerly 67e0d17aed [formerly dad0108065ac5f208f59ce6bc6493000278e2c21]]
Former-commit-id: 8b4d47c690
Former-commit-id: 0038138fdc
This commit is contained in:
Max Schenkelberg 2013-05-16 10:54:16 -05:00
parent b9bfcead05
commit 91d38b1e87
4 changed files with 344 additions and 95 deletions

View file

@ -27,6 +27,7 @@ import org.eclipse.jface.action.IMenuManager;
import com.raytheon.uf.viz.core.DrawableString;
import com.raytheon.uf.viz.core.IDisplayPane;
import com.raytheon.uf.viz.core.IDisplayPaneContainer;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
@ -37,10 +38,12 @@ import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.legend.ILegendDecorator;
import com.raytheon.uf.viz.core.rsc.AbstractResourceData;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IInputHandler.InputPriority;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.RenderingOrderFactory.ResourceOrder;
import com.raytheon.viz.ui.cmenu.ContextMenuManager;
import com.raytheon.viz.ui.cmenu.IContextMenuProvider;
import com.raytheon.viz.ui.input.InputAdapter;
/**
* Base legend resource class, does majority of work for drawing legends.
@ -68,6 +71,56 @@ public abstract class AbstractLegendResource<T extends AbstractResourceData>
private static final int RIGHT_OFFSET_IN_PIXELS = 18;
private InputAdapter resourceClickedHandler = new InputAdapter() {
private boolean moved = false;
private ResourcePair mouseDownRsc;
@Override
public boolean handleMouseDown(int x, int y, int mouseButton) {
boolean handle = moved = false;
IDisplayPaneContainer container = getResourceContainer();
IDisplayPane active = container.getActiveDisplayPane();
if (active.getDescriptor() == getDescriptor()) {
mouseDownRsc = checkLabelSpace(getDescriptor(),
active.getTarget(), x, y);
if (mouseDownRsc != null) {
handle = AbstractLegendResource.this.checkResourceClick(
mouseDownRsc, mouseButton);
if (!handle) {
mouseDownRsc = null;
}
}
}
return handle;
}
@Override
public boolean handleMouseDownMove(int x, int y, int mouseButton) {
if (mouseDownRsc != null) {
moved = true;
}
return moved;
}
@Override
public boolean handleMouseUp(int x, int y, int mouseButton) {
if (mouseDownRsc != null) {
try {
if (!moved) {
resourceClicked(mouseDownRsc, mouseButton);
}
} finally {
mouseDownRsc = null;
}
return true;
}
return false;
}
};
/**
* @param resourceData
* @param loadProperties
@ -84,7 +137,10 @@ public abstract class AbstractLegendResource<T extends AbstractResourceData>
*/
@Override
protected void disposeInternal() {
IDisplayPaneContainer container = getResourceContainer();
if (container != null) {
container.unregisterMouseHandler(resourceClickedHandler);
}
}
/*
@ -148,7 +204,11 @@ public abstract class AbstractLegendResource<T extends AbstractResourceData>
*/
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
IDisplayPaneContainer container = getResourceContainer();
if (container != null) {
container.registerMouseHandler(resourceClickedHandler,
InputPriority.SYSTEM_RESOURCE);
}
}
protected ResourcePair checkLabelSpace(IDescriptor descriptor,
@ -289,4 +349,14 @@ public abstract class AbstractLegendResource<T extends AbstractResourceData>
ContextMenuManager.fillContextMenu(menuManager, selectedResource,
getResourceContainer());
}
protected boolean checkResourceClick(ResourcePair mouseDownRsc,
int mouseButton) {
// Do nothing
return false;
}
protected void resourceClicked(ResourcePair resource, int mouseButton) {
// Do nothing
}
}

View file

@ -30,10 +30,16 @@ import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.GenericResourceData;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.capabilities.BlendableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.BlendedCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.EditableCapability;
import com.raytheon.viz.ui.input.EditableManager;
import com.raytheon.viz.ui.input.preferences.MousePreferenceManager;
/**
* TODO Add Description
* Default legend resource, handles visibility toggling and editableness
* toggling. TODO: Make D2DLegendResource extend it
*
* <pre>
*
@ -52,6 +58,13 @@ import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
public class DefaultLegendResource extends
AbstractLegendResource<GenericResourceData> {
private static final String HIDE_RESOURCE_PREF = "com.raytheon.viz.d2d.ui.hide.resource";
private static final String EDIT_RESOURCE_PREF = "com.raytheon.viz.ui.input.resource.edit";
private MousePreferenceManager prefManager = MousePreferenceManager
.getInstance();
/**
* @param resourceData
* @param loadProperties
@ -94,6 +107,12 @@ public class DefaultLegendResource extends
} else {
legend.label = rsc.getName();
legend.resource = resourcePair;
if (rsc.hasCapability(EditableCapability.class)
&& rsc.getCapability(EditableCapability.class)
.isEditable()) {
legend.label += " (Editable)";
}
}
if (!vis) {
@ -115,4 +134,82 @@ public class DefaultLegendResource extends
}
return entries;
}
@Override
protected boolean checkResourceClick(ResourcePair mouseDownRsc,
int mouseButton) {
return true;
}
@Override
protected void resourceClicked(ResourcePair resource, int mouseButton) {
if (prefManager.handleClick(EDIT_RESOURCE_PREF, mouseButton)
&& resource.getResource().hasCapability(
EditableCapability.class)) {
// Editable case
toggleEditability(resource);
} else {
// Visibility case
toggleVisibility(resource);
}
issueRefresh();
}
/**
* Toggle visibility of resource taking blended/blendable resources into
* account.
*
* If resource to toggle is blended, 1st check to see if parent resource is
* visible. If not visible then make parent and all children visible.
*
* @param rp
*/
protected boolean toggleVisibility(ResourcePair rp) {
AbstractVizResource<?, ?> rsc = rp.getResource();
if (rsc != null) {
if (rsc.hasCapability(BlendedCapability.class)) {
ResourcePair parentRsc = rsc.getCapability(
BlendedCapability.class).getBlendableResource();
ResourceList children = parentRsc.getResource()
.getCapability(BlendableCapability.class)
.getResourceList();
if (parentRsc.getProperties().isVisible() == false) {
parentRsc.getProperties().setVisible(true);
for (ResourcePair child : children) {
child.getProperties().setVisible(true);
}
} else {
// topmost resource is visible, toggle us and other
// rsc
if (rp.getProperties().isVisible() == false) {
rp.getProperties().setVisible(true);
parentRsc
.getResource()
.getCapability(BlendableCapability.class)
.setAlphaStep(BlendableCapability.BLEND_MAX / 2);
} else {
parentRsc.getResource()
.getCapability(BlendableCapability.class)
.toggle(rp);
}
}
return rp.getProperties().isVisible();
}
}
rp.getProperties().setVisible(!rp.getProperties().isVisible());
return rp.getProperties().isVisible();
}
/**
* Toggles the editable flag on the resource
*
* @param rp
* @return
*/
protected boolean toggleEditability(ResourcePair rp) {
EditableManager.makeEditable(rp.getResource(), !rp.getResource()
.getCapability(EditableCapability.class).isEditable());
return rp.getResource().getCapability(EditableCapability.class)
.isEditable();
}
}

View file

@ -21,7 +21,6 @@ package com.raytheon.uf.viz.core.rsc;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -58,14 +57,48 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
extends AbstractVizResource<T, D> {
private static class Frame {
boolean disposed = false;
Object lock = new Object();
List<PluginDataObject> records = new ArrayList<PluginDataObject>();
IRenderable renderable;
}
private Map<DataTime, Frame> frames = new HashMap<DataTime, Frame>();
private IResourceDataChanged dataChangedListener = new IResourceDataChanged() {
@Override
public void resourceChanged(ChangeType type, Object object) {
if (type == ChangeType.DATA_UPDATE) {
if (object instanceof PluginDataObject) {
addDataObject((PluginDataObject) object);
} else if (object instanceof PluginDataObject[]) {
addDataObject((PluginDataObject[]) object);
} else if (object instanceof Object[]) {
List<PluginDataObject> pdos = new ArrayList<PluginDataObject>();
for (Object obj : (Object[]) object) {
if (obj instanceof PluginDataObject) {
pdos.add((PluginDataObject) obj);
}
}
if (pdos.size() > 0) {
addDataObject(pdos.toArray(new PluginDataObject[0]));
}
}
} else if (type == ChangeType.CAPABILITY) {
if (object instanceof AbstractCapability) {
AbstractCapability capability = (AbstractCapability) object;
for (Frame frame : frames.values()) {
if (frame.renderable != null) {
capabilityChanged(frame.renderable, capability);
}
}
}
}
}
};
private Object lock = new Object();
private Map<DataTime, Frame> frames = new HashMap<DataTime, Frame>();
/**
* @param resourceData
@ -75,35 +108,6 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
LoadProperties loadProperties) {
super(resourceData, loadProperties);
dataTimes = new ArrayList<DataTime>();
resourceData.addChangeListener(new IResourceDataChanged() {
@Override
public void resourceChanged(ChangeType type, Object object) {
if (type == ChangeType.DATA_UPDATE) {
if (object instanceof PluginDataObject) {
addDataObject((PluginDataObject) object);
} else if (object instanceof PluginDataObject[]) {
for (PluginDataObject pdo : (PluginDataObject[]) object) {
addDataObject((PluginDataObject) pdo);
}
} else if (object instanceof Object[]) {
for (Object obj : (Object[]) object) {
if (obj instanceof PluginDataObject) {
addDataObject((PluginDataObject) obj);
}
}
}
} else if (type == ChangeType.CAPABILITY) {
if (object instanceof AbstractCapability) {
AbstractCapability capability = (AbstractCapability) object;
for (Frame frame : frames.values()) {
if (frame.renderable != null) {
capabilityChanged(frame.renderable, capability);
}
}
}
}
}
});
}
/**
@ -112,30 +116,50 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
*
* @param pdo
*/
protected final void addDataObject(PluginDataObject pdo) {
synchronized (lock) {
if (frames == null) {
// Check for null in case we were waiting for lock from
// disposeInternal in which case we shouldn't process add
protected final void addDataObject(PluginDataObject... pdos) {
// Organize PDOs by time
Map<DataTime, List<PluginDataObject>> pdoMap = new HashMap<DataTime, List<PluginDataObject>>();
for (PluginDataObject pdo : pdos) {
DataTime time = getDataObjectTime(pdo);
List<PluginDataObject> list = pdoMap.get(time);
if (list == null) {
list = new ArrayList<PluginDataObject>();
pdoMap.put(time, list);
}
list.add(pdo);
}
synchronized (this) {
if (getStatus() == ResourceStatus.DISPOSED) {
// Don't process if disposed
return;
}
DataTime time = getDataObjectTime(pdo);
Frame frame = frames.get(time);
if (frame == null) {
frame = new Frame();
frames.put(time, frame);
}
if (frame.records.contains(pdo)) {
frame.records.remove(pdo);
}
frame.records.add(pdo);
if (frame.renderable != null) {
if (updateRenderable(frame.renderable, pdo) == false) {
dispose(frame.renderable);
for (DataTime time : pdoMap.keySet()) {
Frame frame = frames.get(time);
if (frame == null) {
frame = new Frame();
frames.put(time, frame);
}
synchronized (frame.lock) {
List<PluginDataObject> pdoList = pdoMap.get(time);
for (PluginDataObject pdo : pdoList) {
if (frame.records.contains(pdo)) {
frame.records.remove(pdo);
}
frame.records.add(pdo);
}
if (frame.renderable != null) {
if (updateRenderable(frame.renderable,
pdoList.toArray(new PluginDataObject[0])) == false) {
dispose(frame.renderable);
}
}
}
if (!dataTimes.contains(dataTimes)) {
dataTimes.add(time);
}
}
if (!dataTimes.contains(dataTimes)) {
dataTimes.add(time);
}
}
}
@ -159,9 +183,14 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
* @return
*/
protected List<PluginDataObject> getPluginDataObjects(DataTime time) {
Frame frame = frames.get(time);
Frame frame = null;
synchronized (this) {
frame = frames.get(time);
}
if (frame != null) {
return frame.records;
synchronized (frame.lock) {
return new ArrayList<PluginDataObject>(frame.records);
}
}
return new ArrayList<PluginDataObject>();
}
@ -185,14 +214,13 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
*/
@Override
public final void remove(DataTime dataTime) {
synchronized (lock) {
Frame frame = null;
synchronized (this) {
super.remove(dataTime);
Frame frame = frames.remove(dataTime);
if (frame != null && frame.renderable != null) {
IRenderable dispose = frame.renderable;
frame.renderable = null;
dispose(dispose);
}
frame = frames.remove(dataTime);
}
if (frame != null) {
disposeFrame(frame);
}
}
@ -213,19 +241,39 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
@Override
public final void project(CoordinateReferenceSystem crs)
throws VizException {
Iterator<Frame> iter = frames.values().iterator();
while (iter.hasNext()) {
Frame frame = iter.next();
IRenderable renderable = frame.renderable;
if (renderable != null) {
if (!projectRenderable(renderable, crs)) {
frame.renderable = null;
dispose(renderable);
Map<DataTime, Frame> frames = null;
synchronized (this) {
frames = new HashMap<DataTime, Frame>(this.frames);
}
for (Frame frame : frames.values()) {
synchronized (frame.lock) {
IRenderable renderable = frame.renderable;
if (renderable != null) {
if (!projectRenderable(renderable, crs)) {
frame.renderable = null;
dispose(renderable);
}
}
}
}
}
@Override
protected final void initInternal(IGraphicsTarget target)
throws VizException {
resourceData.addChangeListener(dataChangedListener);
initResource(target);
}
/**
* Init method for the resource to initialize any data needed for rendering
* which is not tied to a renderable.
*/
protected void initResource(IGraphicsTarget target) throws VizException {
}
/*
* (non-Javadoc)
*
@ -240,16 +288,28 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
DataTime time = paintProps.getDataTime();
if (time == null) {
time = getTimeForResource();
}
Frame currFrame = frames.get(time);
if (currFrame != null) {
IRenderable renderable = currFrame.renderable;
if (renderable == null) {
currFrame.renderable = renderable = constructRenderable(currFrame.records);
if (time == null) {
return;
}
}
Frame currFrame = null;
synchronized (this) {
currFrame = frames.get(time);
}
if (currFrame != null) {
synchronized (currFrame.lock) {
if (currFrame.disposed == false) {
IRenderable renderable = currFrame.renderable;
if (renderable == null) {
currFrame.renderable = renderable = constructRenderable(
time, new ArrayList<PluginDataObject>(
currFrame.records));
}
if (renderable != null) {
renderable.paint(target, paintProps);
if (renderable != null) {
renderable.paint(target, paintProps);
}
}
}
}
}
@ -261,18 +321,39 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
*/
@Override
protected final void disposeInternal() {
synchronized (lock) {
for (Frame frame : frames.values()) {
if (frame.renderable != null) {
disposeRenderable(frame.renderable);
}
}
frames.clear();
frames = null;
Map<DataTime, Frame> frames = null;
synchronized (this) {
// Copy Frames and clear member
frames = new HashMap<DataTime, Frame>(this.frames);
this.frames.clear();
}
// Dispose frame one by one
for (Frame frame : frames.values()) {
disposeFrame(frame);
}
resourceData.removeChangeListener(dataChangedListener);
disposeResource();
}
/**
* Disposes the Frame object by disposing the renderable and marking
* disposed
*
* @param frame
*/
private void disposeFrame(Frame frame) {
synchronized (frame.lock) {
if (frame.renderable != null) {
disposeRenderable(frame.renderable);
frame.renderable = null;
}
frame.records.clear();
frame.disposed = true;
}
}
/**
* Dispose method for the resource to dispose any data not tied to a
* renderable. Called after all renderables have been disposed. Default impl
@ -320,21 +401,22 @@ public abstract class AbstractPluginDataObjectResource<T extends AbstractResourc
* NOTE: The size of the pdo list will only grow so it can be used to
* determine if new data has arrived since last call
*
* @param time
* @param records
* @return
*/
protected abstract IRenderable constructRenderable(
protected abstract IRenderable constructRenderable(DataTime time,
List<PluginDataObject> records) throws VizException;
/**
* Update the renderable with the new pdo, if the renderable is updatable,
* Update the renderable with the new pdos, if the renderable is updatable,
* return true. If the renderable needs to be recreated from scratch, return
* false
*
* @param renderable
* @param pdo
* @param pdos
* @return
*/
protected abstract boolean updateRenderable(IRenderable renderable,
PluginDataObject pdo);
PluginDataObject... pdos);
}

View file

@ -18,8 +18,8 @@
</license>
<requires>
<import feature="com.raytheon.uf.viz.cots.feature" version="1.0.0.qualifier"/>
<import feature="com.raytheon.uf.viz.common.core.feature" version="1.0.0.qualifier"/>
<import feature="com.raytheon.uf.common.base.feature" version="1.0.0.qualifier"/>
<import feature="com.raytheon.uf.viz.base.feature" version="1.0.0.qualifier"/>
</requires>
<plugin