All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scijava.ops.engine.yaml.impl.YAMLOpClassInfo Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * Java implementation of the SciJava Ops matching engine.
 * %%
 * Copyright (C) 2016 - 2024 SciJava developers.
 * %%
 * 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.scijava.ops.engine.yaml.impl;

import org.scijava.common3.Annotations;
import org.scijava.common3.Classes;
import org.scijava.ops.api.OpInfo;
import org.scijava.ops.engine.OpDependencyMember;
import org.scijava.ops.engine.exceptions.impl.FinalOpDependencyFieldException;
import org.scijava.ops.engine.struct.FieldOpDependencyMember;
import org.scijava.ops.engine.struct.FunctionalParameters;
import org.scijava.ops.engine.struct.SynthesizedParameterMember;
import org.scijava.ops.engine.util.Infos;
import org.scijava.ops.spi.OpDependency;
import org.scijava.struct.Member;
import org.scijava.struct.Struct;
import org.scijava.struct.StructInstance;
import org.scijava.common3.Types;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * An {@link OpInfo}, backed by some {@code public} {@link Class}, described via
 * YAML that is stored within a {@link Map}.
 *
 * @author Gabriel Selzer
 */
public class YAMLOpClassInfo extends AbstractYAMLOpInfo {

	private final Class cls;
	private final Struct struct;

	public YAMLOpClassInfo( //
		final Map yaml, //
		final String identifier)
	{
		super(yaml, identifier);
		// parse class
		this.cls = Classes.load(identifier);
		this.struct = createStruct(yaml);

		// Validate general OpInfo features
		Infos.validate(this);
	}

	// -- OpInfo methods --

	@Override
	public Type opType() {
		return Types.parameterize(cls);
	}

	@Override
	public Struct struct() {
		return struct;
	}

	@Override
	public String implementationName() {
		return cls.getName();
	}

	@Override
	public AnnotatedElement getAnnotationBearer() {
		return cls;
	}

	/**
	 * For a {@link Class}, we define the implementation as the concatenation of:
	 * 
    *
  1. The fully qualified name of the class
  2. *
  3. The version of the class containing the field, with a preceding * {@code @}
  4. *
*

* For example, for a field class {@code com.example.foo.Bar}, you might have *

* {@code [email protected]} *

*/ @Override public String id() { return OpInfo.IMPL_DECLARATION + implementationName() + "@" + version(); } private Struct createStruct(Map yaml) { List> members = new ArrayList<>(); List> params = (List>) yaml.get( "parameters"); var fmts = FunctionalParameters.findFunctionalMethodTypes(cls); for (int i = 0; i < params.size(); i++) { var pMap = params.get(i); var fmt = fmts.get(i); String name = (String) pMap.get("name"); String description = (String) pMap.get("description"); boolean nullable = (boolean) pMap.getOrDefault("nullable", false); members.add(new SynthesizedParameterMember<>(fmt, name, !nullable, description)); } // Add Op Dependencies final List fields = Annotations.annotatedFields(cls, OpDependency.class); for (final Field f : fields) { f.setAccessible(true); if (Modifier.isFinal(f.getModifiers())) { // Final fields are bad because they cannot be modified. throw new FinalOpDependencyFieldException(f); } final FieldOpDependencyMember item = new FieldOpDependencyMember<>(f, cls); members.add(item); } return () -> members; } @Override public StructInstance createOpInstance(List dependencies) { final Object op; try { Constructor ctor = cls.getDeclaredConstructor(); ctor.setAccessible(true); op = ctor.newInstance(); } catch (final InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) { throw new IllegalStateException("Unable to instantiate op: '" + cls .getName() + "' Ensure that the Op has a no-args constructor.", e); } final var dependencyMembers = Infos.dependencies(this); for (int i = 0; i < dependencyMembers.size(); i++) { final OpDependencyMember dependencyMember = dependencyMembers.get(i); try { dependencyMember.createInstance(op).set(dependencies.get(i)); } catch (final Exception ex) { // TODO: Improve error message. Used to include exact OpRequest of Op // dependency. throw new IllegalStateException( "Exception trying to inject Op dependency field.\n" + "\tOp dependency field to resolve: " + dependencyMember.key() + "\n" + "\tFound Op to inject: " + dependencies.get(i).getClass() .getName() + // "\n" + "\tField signature: " + dependencyMember.type(), ex); } } return struct().createInstance(op); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy