/*
* #%L
* ImageJ software for multidimensional image processing and analysis.
* %%
* Copyright (C) 2014 - 2015 Board of Regents of the University of
* Wisconsin-Madison, University of Konstanz and Brian Northan.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.knime.knip.features.sets.optimizedfeatures;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import org.scijava.Priority;
import org.scijava.cache.CacheService;
import org.scijava.command.CommandInfo;
import org.scijava.module.Module;
import org.scijava.module.ModuleItem;
import org.scijava.plugin.Parameter;
import net.imagej.ops.AbstractOp;
import net.imagej.ops.CustomOpEnvironment;
import net.imagej.ops.Op;
import net.imagej.ops.OpEnvironment;
import net.imagej.ops.OpInfo;
import net.imagej.ops.OpRef;
import net.imagej.ops.special.function.UnaryFunctionOp;
import net.imagej.ops.special.hybrid.UnaryHybridCF;
/**
* Creates {@link CachedFunctionOp}s which know how to cache their outputs.
*
* @author Christian Dietz, University of Konstanz
* @deprecated CAN BE REMOVED AS SOON AS CONVERTER BUG IN OPS WAS RESOLVED (see
* Geometric3DFeatureSet)
*/
public class KNIPCachedOpEnvironment extends CustomOpEnvironment {
@Parameter
private CacheService cs;
private Collection<Class<?>> ignored;
public KNIPCachedOpEnvironment(final OpEnvironment parent, final Collection<? extends OpInfo> prioritizedInfos,
final Collection<Class<?>> ignored) {
super(parent, prioritizedInfos);
if (prioritizedInfos != null)
for (final OpInfo info : prioritizedInfos) {
info.cInfo().setPriority(Priority.FIRST_PRIORITY);
}
this.ignored = ignored;
}
@Override
public Op op(final OpRef ref) {
try {
final Op op = super.op(ref);
for (final Class<?> ignored : ignored) {
for (final Type t : ref.getTypes()) {
if (ignored.isAssignableFrom(Class.forName(t.getTypeName()))) {
return op;
}
}
}
final Op cachedOp;
if (op instanceof UnaryHybridCF) {
cachedOp = wrapUnaryHybrid((UnaryHybridCF<?, ?>) op);
} else if (op instanceof UnaryFunctionOp) {
cachedOp = wrapUnaryFunction((UnaryFunctionOp<?, ?>) op);
} else
return op;
getContext().inject(cachedOp);
return cachedOp;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
// -- Helper methods --
private <I, O> CachedFunctionOp<I, O> wrapUnaryFunction(final UnaryFunctionOp<I, O> op) {
return new CachedFunctionOp<>(op, otherArgs(op, 1));
}
private <I, O> CachedHybridOp<I, O> wrapUnaryHybrid(final UnaryHybridCF<I, O> op) {
return new CachedHybridOp<>(op, otherArgs(op, 2));
}
/**
* Gets the given {@link Op} instance's argument value, starting at the
* specified offset.
*/
private Object[] otherArgs(final Op op, final int offset) {
final CommandInfo cInfo = info(op).cInfo();
final Module module = cInfo.createModule(op);
final ArrayList<Object> args = new ArrayList<>();
int i = 0;
for (final ModuleItem<?> input : cInfo.inputs()) {
if (i++ >= offset)
args.add(input.getValue(module));
}
return args.toArray();
}
// -- Helper classes --
/**
* Wraps a {@link UnaryFunctionOp} and caches the results. New inputs will
* result in re-computation of the result.
*
* @author Christian Dietz, University of Konstanz
* @param <I>
* @param <O>
*/
public class CachedFunctionOp<I, O> extends AbstractOp implements UnaryFunctionOp<I, O> {
@Parameter
private CacheService cache;
private final UnaryFunctionOp<I, O> delegate;
private final Object[] args;
public CachedFunctionOp(final UnaryFunctionOp<I, O> delegate, final Object[] args) {
this.delegate = delegate;
this.args = args;
}
@Override
public O calculate(final I input) {
final Hash hash = new Hash(input, delegate, args);
@SuppressWarnings("unchecked")
O output = (O) cache.get(hash);
if (output == null) {
output = delegate.calculate(input);
cache.put(hash, output);
}
return output;
}
@Override
public void run() {
delegate.run();
}
@Override
public I in() {
return delegate.in();
}
@Override
public void setInput(I input) {
delegate.setInput(input);
}
@Override
public O out() {
return delegate.out();
}
@Override
public void initialize() {
delegate.initialize();
}
@Override
public CachedFunctionOp<I, O> getIndependentInstance() {
return this;
}
public Class<?> getDelegateType() {
return delegate.getClass();
}
}
/**
* Wraps a {@link UnaryHybridOp} and caches the results. New inputs will
* result in re-computation if {@link UnaryHybridOp} is used as
* {@link UnaryFunctionOp}.
*
* @author Christian Dietz, University of Konstanz
* @param <I>
* @param <O>
*/
public class CachedHybridOp<I, O> extends CachedFunctionOp<I, O> implements UnaryHybridCF<I, O> {
@Parameter
private CacheService cache;
private final UnaryHybridCF<I, O> delegate;
private final Object[] args;
public CachedHybridOp(final UnaryHybridCF<I, O> delegate, final Object[] args) {
super(delegate, args);
this.delegate = delegate;
this.args = args;
}
@Override
public O calculate(final I input) {
final Hash hash = new Hash(input, delegate, args);
@SuppressWarnings("unchecked")
O output = (O) cache.get(hash);
if (output == null) {
output = createOutput(input);
compute(input, output);
cache.put(hash, output);
}
return output;
}
@Override
public O createOutput(I input) {
return delegate.createOutput(input);
}
@Override
public void compute(final I input, final O output) {
delegate.compute(input, output);
}
@Override
public void setOutput(final O output) {
delegate.setOutput(output);
}
@Override
public CachedHybridOp<I, O> getIndependentInstance() {
return this;
}
}
/**
* Simple utility class to wrap two objects and an array of objects in a
* single object which combines their hashes.
*/
private class Hash {
private final int hash;
public Hash(final Object o1, final Object o2, final Object[] args) {
long h = o1.hashCode() ^ o2.getClass().getSimpleName().hashCode();
for (final Object o : args) {
h ^= o.hashCode();
}
hash = (int) h;
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(final Object obj) {
if (obj == this)
return true;
if (obj instanceof Hash)
return hash == ((Hash) obj).hash;
return false;
}
}
}