/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2015, Geomatys
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotoolkit.gui.javafx.parameter;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.opengis.feature.AttributeType;
import org.opengis.parameter.ParameterDescriptor;
import org.apache.sis.internal.system.SystemListener;
/**
* A service provider for javaFx editors of values which can be defined by a
* {@link ParameterDescriptor} or {@link AttributeType}.
*
* @author Alexis Manin (Geomatys)
* @author Johann Sorel (Geomatys)
*/
public abstract class FXValueEditorSpi {
/** List of all registered Spis in current context. */
private static final ServiceLoader<FXValueEditorSpi> SERVICE_LOADER = ServiceLoader.load(FXValueEditorSpi.class);
/**
* Service loader can be reloaded / accessed from multiple threads, we have to synnchronize its accesses.
*/
private static final ReentrantReadWriteLock CONTEXT_LOCK = new ReentrantReadWriteLock();
/**
* Listen on classpath changes (due to OSGi, etc.) to reload service loader and so, be aware of new editors arrival.
*/
private static final SystemListener CONTEXT_LISTENER = new SystemListener("geotk-widgets-javafx") {
@Override
protected void classpathChanged() {
CONTEXT_LOCK.writeLock().lock();
try {
SERVICE_LOADER.reload();
} finally {
CONTEXT_LOCK.writeLock().unlock();
}
}
};
static {
SystemListener.add(CONTEXT_LISTENER);
}
/**
* Check if the current provider can work on values whose definition is compatible
* with given attribute type.
* @param property The type of the property to edit.
* @return True if Spi can provide an editor matching given type, false otherwise.
*/
public boolean canHandle(AttributeType property){
return canHandle(property.getValueClass());
}
/**
* Check if the current provider can work on values whose definition is compatible
* with given descriptor.
* @param param The descriptor of the property to edit.
* @return True if Spi can provide an editor matching given type, false otherwise.
*/
public boolean canHandle(ParameterDescriptor param){
return canHandle(param.getValueClass());
}
/**
* Check if current Spi can provide objects of input class.
* @param binding The type of object which must be provided by an editor.
* @return True if this Spi can create an editor which will work on objects
* of given type.
*/
public abstract boolean canHandle(Class binding);
/**
* Search an editor capable of editing values compliant with input attribute type.
*
* @param target The attribute type describing the value to edit.
* @return An editor matching given type, or an empty optional.
*/
public static final Optional<FXValueEditor> findEditor(final AttributeType target) {
CONTEXT_LOCK.readLock().lock();
try {
for (final FXValueEditorSpi spi : SERVICE_LOADER) {
if (spi.canHandle(target)) {
final FXValueEditor editor = spi.createEditor();
editor.setAttributeType(target);
return Optional.of(editor);
}
}
} finally {
CONTEXT_LOCK.readLock().unlock();
}
return Optional.empty();
}
/**
* Search an editor capable of editing values compliant with input descriptor.
*
* @param target The descriptor of the parameter to edit.
* @return An editor matching given type, or an empty optional.
*/
public static final Optional<FXValueEditor> findEditor(final ParameterDescriptor target) {
CONTEXT_LOCK.readLock().lock();
try {
for (final FXValueEditorSpi spi : SERVICE_LOADER) {
if (spi.canHandle(target)) {
final FXValueEditor editor = spi.createEditor();
editor.setParamDesc(target);
return Optional.of(editor);
}
}
} finally {
CONTEXT_LOCK.readLock().unlock();
}
return Optional.empty();
}
/**
* @return an editor provided by current Spi.
*/
public abstract FXValueEditor createEditor();
}