Step 3: Creating a Custom Field Descriptor

Project Picker CF

Let's say you need to provide support for a certain custom field. Power Scripts™ for Jira does offer support for the project picker CF but for educational purposes, let's see how you could add the support if it didn't.



The task is to register a translator from SILValue to the native object type and the other way around.

The code is presented below:

ProjectCFDescriptor.java
/* * Created at: 3/28/13, 3:02 PM * * File: ProjectCFDescriptor.java */ package com.mycompany.silexample; import com.atlassian.jira.project.Project; import com.atlassian.jira.project.ProjectManager; import com.keplerrominfo.sil.lang.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.ofbiz.core.entity.GenericValue; import com.keplerrominfo.common.util.MutableString; import com.keplerrominfo.common.util.StringUtils; import com.keplerrominfo.jira.commons.ivm.FieldDescriptor; import com.keplerrominfo.sil.lang.type.TypeInst; /** * The project CF descriptor, com.atlassian.jira.plugin.system.customfieldtypes:project * * @author Radu Dumitriu (rdumitriu@gmail.com) */ public class ProjectCFDescriptor implements FieldDescriptor<GenericValue, MutableString> { //This template class tells us that the values exchanged are GenericValue (JIRA side) and MutableString (SIL) private static final Log LOG = LogFactory.getLog(ProjectCFDescriptor.class); private final ProjectManager projectManager; public ProjectCFDescriptor(ProjectManager projectManager) { this.projectManager = projectManager; } @Override public SILType<MutableString> getType() { return TypeInst.STRING; } @Override public GenericValue toJiraValue(SILContext ctx, SILValue<MutableString> value) { String projectKey = StringUtils.trim(value.toStringValue()); if (projectKey == null) { return null; } Project project = projectManager.getProjectObjByKey(projectKey); if(project == null) { LOG.warn(String.format(">>%s<< is not a valid project key", projectKey)); return null; } // Project picker CF stores the value as GenericValue. This is deprecated, but we need to call it return project.getGenericValue(); } @Override public SILValue<MutableString> toSILValue(SILContext ctx, GenericValue value) { return value != null ? SILValueFactory.string(value.getString("key")) : SILValueFactory.string(); } }



Register a CF descriptor in the launcher:

BundleActivator.java
@Override public void doAtLaunch() { super.doAtLaunch(); //register the routine RoutineRegistry.register(new ReverseStringRoutine("myReverseString")); //register the CF descriptor customFieldDescriptorRegistry.register("com.atlassian.jira.plugin.system.customfieldtypes:project", //this is the real key of the CF new AbstractCustomFieldDescriptorFactory( "example.project.cfdf", //internal identification. Must be unique in the SIL system "Project Key to String", //short description, followed by a longer one "Extract the project key using GenericValue or project") { // Descriptors are created when needed, and some of them might be contextual. This is why you register here a creator of a // descriptor and not the real descriptor @Override public FieldDescriptor createDescriptor(Issue issue, CustomField cf) { return new ProjectCFDescriptor(projectManager); } } ); } @Override public void doAtStop() { //first, make sure that super is called, even if it has an exception here try { //unregister the routine RoutineRegistry.unregister("myReverseString"); //unregister the CF descriptor customFieldDescriptorRegistry.unregister("com.atlassian.jira.plugin.system.customfieldtypes:project"); //this is the real key of the CF } catch(Exception e) { LOG.error("Failed to unregister!", e); } super.doAtStop(); }



Line 9 registers the CF into our CF translators map. Hooray!



That's all you need. You can now play with SIL™ like such:

something.sil
summary += " -- " + KProject; //where KProject is your CF name!