blob: a8134adfd13f473109a0b49001d6d27d4f150e8d [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2011, 2018 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.rj.eclient.graphics.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.ecommons.ts.core.SystemRunnable;
import org.eclipse.statet.ecommons.ts.core.Tool;
import org.eclipse.statet.ecommons.ts.core.ToolService;
import org.eclipse.statet.rj.eclient.graphics.ERGraphic;
import org.eclipse.statet.rj.eclient.graphics.LocatorCallback;
/**
* Basic implementation for a local locator for R graphics.
* <p>
* Requests the user to locate point and converts the graphic coordinates to user coordinates.
* It uses the tool service API (org.eclipse.statet.ecommons.ts.core) to schedule the R job.</p>
*/
public abstract class AbstractLocalLocator extends LocatorCallback implements SystemRunnable {
protected static final ImList<String> OK_CANCEL_STOP_TYPES= ImCollections.newList(
ERGraphic.LOCATOR_DONE, ERGraphic.LOCATOR_CANCEL );
private final ERGraphic graphic;
private final Tool tool;
private boolean started;
private boolean graphicLocatorStarted;
private boolean runnableScheduled;
private final List<double[]> toConvert= new ArrayList<>();
private final List<double[]> locatedGraphic= new ArrayList<>();
private final List<double[]> locatedUser= new ArrayList<>();
private volatile int counter;
public AbstractLocalLocator(final ERGraphic graphic) {
this.graphic= graphic;
this.tool= graphic.getRHandle();
}
@Override
public Collection<String> getStopTypes() {
return OK_CANCEL_STOP_TYPES;
}
@Override
public String getMessage() {
return super.getMessage() + " (" + this.counter + " selected)";
}
@Override
public int located(final double x, final double y) {
synchronized (this) {
this.counter++;
this.toConvert.add(new double[] { x, y });
if (!internalScheduleConversion()) {
internalStop(null);
return STOP;
}
}
return NEXT;
}
@Override
public void stopped(final String type) {
synchronized (this) {
this.graphicLocatorStarted= false;
if (type == ERGraphic.LOCATOR_DONE) {
if (this.toConvert.isEmpty()) {
internalStop(ERGraphic.LOCATOR_DONE);
return;
}
else if (internalScheduleConversion()) {
return;
}
// scheduling failed
}
internalStop(null);
return;
}
}
@Override
public String getTypeId() {
return "r/rjgd/locallocator"; //$NON-NLS-1$
}
@Override
public String getLabel() {
return "Resolve Graphic Points";
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == this.tool);
}
@Override
public boolean changed(final int event, final Tool tool) {
switch (event) {
case REMOVING_FROM:
return false;
case BEING_ABANDONED:
case FINISHING_CANCEL:
case FINISHING_ERROR:
synchronized (this) {
this.runnableScheduled= false;
if (!this.graphicLocatorStarted) {
internalStop(null);
break;
}
}
this.graphic.stopLocator(null);
break;
case FINISHING_OK:
synchronized (this) {
if (!this.graphicLocatorStarted && this.toConvert.isEmpty()) {
internalStop(ERGraphic.LOCATOR_DONE);
break;
}
}
}
return true;
}
@Override
public void run(final ToolService service,
final IProgressMonitor monitor) throws CoreException {
double[] graphic= null;
double[] user= null;
while (true) {
synchronized (this) {
if (graphic != null) {
if (user != null) {
this.locatedGraphic.add(graphic);
this.locatedUser.add(user);
}
else {
// invalid point?
}
}
if (this.toConvert.isEmpty()) {
this.counter= this.locatedGraphic.size();
this.runnableScheduled= false;
return;
}
graphic= this.toConvert.remove(0);
user= null;
}
user= this.graphic.convertGraphic2User(graphic, monitor);
}
}
/**
* synchronized
*/
protected boolean internalScheduleConversion() {
if (!this.runnableScheduled) {
if (this.tool.getQueue().addHot(this).isOK()) {
this.runnableScheduled= true;
return true;
}
return false;
}
return true;
}
/**
* synchronized
*
* @param type the stop type
*/
protected void internalStop(final String type) {
assert (!this.graphicLocatorStarted);
if (this.runnableScheduled) {
this.tool.getQueue().removeHot(this);
this.runnableScheduled= false;
}
this.started= false;
if (type == ERGraphic.LOCATOR_DONE) {
finished(new ArrayList<>(this.locatedGraphic), new ArrayList<>(this.locatedUser));
}
else {
canceled();
}
}
public boolean start() {
synchronized (this) {
if (this.started) {
return false;
}
this.toConvert.clear();
this.locatedGraphic.clear();
this.locatedUser.clear();
this.started= this.graphicLocatorStarted= true;
this.counter= 0;
if (this.graphic.startLocalLocator(this).isOK()) {
return true;
}
else {
this.started= this.graphicLocatorStarted= false;
return false;
}
}
}
/**
* Is called if the locator and conversion is finished.
*
* @param graphic the graphic coordinates
* @param user the user coordinates
*/
protected abstract void finished(final List<double[]> graphic, final List<double[]> user);
/**
* Is called if the locator was canceled
*/
protected abstract void canceled();
}