Making Your Class Scriptable

You may have written your own class and wish to make it accessible from the Python interpreter. By default, all classes in the JVM are accessible in the Python interpreter using their fully classified name.

For example, to make the com.mycompany.myviewer.MyClass class accessible, just type:

from com.mycompany.myviewer import MyClass

The Python interpreter has two special commands to browse interactively the catalog of accessible classes that were added for INTViewer.

  • classes
  • methods

Here is a typical output when the "classes" command is entered:

Here is a typical output when the "methods" command is entered:

If you want your class to be accessible in the "classes" and "methods" catalog, there are three main steps:

  • Write a class that implements IScriptable
  • Write an adapter class that references this scriptable class
  • Register your adapter class in the layer.xml file.

Scriptable Class

Any class accessible from the Python interpreter needs to implement com.interactive.intviewerapi.scriptables.IScriptable. You do not need to implement any methods. It is best to write scriptable classes that extend com.interactive.intviewerapi.scriptables.AbstrastScriptable. While making an existing Java class implement IScriptable is possible, it is best to create a separate scriptable class that extends AbstrastScriptable and wraps that existing Java class.

In the example below, we "wrap" the com.interactive.intviewerapi.data.seismic.ITrace class in a scriptable Trace class.

TRACE.JAVA

package com.interactive.intviewerapi.scriptables;
import com.interactive.intviewer.scriptables.ScriptablePropertiesUtil;
import com.interactive.intviewerapi.data.seismic.ITrace;
import com.interactive.intviewerapi.scriptables.IScriptable.Comment;
import java.util.Arrays;
import java.util.Map;
@Comment(comment = "Represents a seismic trace, including all sample and header values")
public class Trace extends AbstractScriptable {
    private ITrace trace;
    private Map<String, Integer> map;
    private Number getHeaderValue(String headerName) {
        Integer fieldId = map.get(headerName);
        if (fieldId == null) {
            throw new IllegalArgumentException("Invalid header name: " + headerName);
        }
        return this.trace.getHeader(fieldId);
    }
    public double getHeader(String headerName) {
        return this.getHeaderValue(headerName).doubleValue();
    }
    public float[] getSamples() {
        float[] floats = new float[trace.getSize()];
        trace.getSamples(floats);
        return floats;
    }
    // standard code
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Trace other = (Trace) obj;
        if (this.trace != other.trace && (this.trace == null || !this.trace.equals(other.trace))) {
            return false;
        }
        return true;
    }
    @Override
    public int hashCode() {
        int hash = 3;
        hash = 97 * hash + (this.trace != null ? this.trace.hashCode() : 0);
        return hash;
    }
    @Override
    public String toString() {
        float[] floats = this.getSamples();
        float[] first10 = Arrays.copyOf(floats, 10);
        String result = ScriptablePropertiesUtil.listToString(ScriptablePropertiesUtil.arrayToList(first10));
        return result + "... (" + floats.length + " samples total)";
    }
    // wrapper code
    private Trace(ITrace trace, Map<String, Integer> map) {
        this.trace = trace;
        this.map = map;
    }
    public static class Wrapper {
        private Wrapper() {
        }
        public static ITrace unwrap(Trace trace) {
            if (trace == null) {
                throw new IllegalArgumentException("Can't unwrap an empty object");
            }
            return trace.trace;
        }
        public static Trace wrap(ITrace trace, Map<String, Integer> maps) {
            if (trace == null) {
                throw new IllegalArgumentException("Can't wrap an empty object");
            }
            return new Trace(trace, maps);
        }
    }
}

Adapter class

TRACESCRIPTABLEADAPTER.JAVA

package com.interactive.intviewer.seismic.data;
import com.interactive.intviewerapi.scriptables.IScriptable;
import com.interactive.intviewerapi.scriptables.Trace;
public class TraceScriptableAdapter implements
        IScriptable.Adapter<Trace> {
    // public empty constructor needed by lookup mechanism
    public TraceScriptableAdapter() {
    }
    @Override
    public Class<Trace> getScriptableClass() {
        return Trace.class;
    }
}

Layer.xml entry

LAYER.XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
   <folder name="ScriptableAdapters">
        <file name="com-interactive-intviewer-seismic-data-TraceScriptableAdapter.instance">
            <attr name="position" intvalue="10020"/>
        </file>
    </folder>
</filesystem>

Once your adapter class is registered in the layer.xml, it should be accessible from the Python terminal window.