extensions/net.sf.basedb.relax/trunk/src/net/sf/basedb/relax/plugins/ReleaseImporterPlugin.java

Code
Comments
Other
Rev Date Author Line
4366 27 Feb 17 nicklas 1 package net.sf.basedb.relax.plugins;
4366 27 Feb 17 nicklas 2
4366 27 Feb 17 nicklas 3 import java.io.ByteArrayOutputStream;
4366 27 Feb 17 nicklas 4 import java.io.IOException;
4366 27 Feb 17 nicklas 5 import java.io.InputStream;
4445 06 Apr 17 nicklas 6 import java.io.Serializable;
4405 20 Mar 17 nicklas 7 import java.net.URI;
4366 27 Feb 17 nicklas 8 import java.util.ArrayList;
4366 27 Feb 17 nicklas 9 import java.util.Arrays;
4366 27 Feb 17 nicklas 10 import java.util.Collections;
4368 28 Feb 17 nicklas 11 import java.util.EnumSet;
4370 28 Feb 17 nicklas 12 import java.util.HashMap;
4368 28 Feb 17 nicklas 13 import java.util.HashSet;
4366 27 Feb 17 nicklas 14 import java.util.List;
4370 28 Feb 17 nicklas 15 import java.util.Map;
4366 27 Feb 17 nicklas 16 import java.util.Set;
4433 29 Mar 17 nicklas 17 import java.util.regex.Matcher;
4433 29 Mar 17 nicklas 18 import java.util.regex.Pattern;
4366 27 Feb 17 nicklas 19
7036 10 Feb 23 nicklas 20 import org.apache.commons.lang3.time.FastDateFormat;
4368 28 Feb 17 nicklas 21 import org.json.simple.JSONArray;
4366 27 Feb 17 nicklas 22 import org.json.simple.JSONObject;
4366 27 Feb 17 nicklas 23 import org.json.simple.parser.JSONParser;
4366 27 Feb 17 nicklas 24
4384 08 Mar 17 nicklas 25 import net.sf.basedb.core.Annotatable;
4384 08 Mar 17 nicklas 26 import net.sf.basedb.core.Annotation;
6784 02 Aug 22 nicklas 27 import net.sf.basedb.core.Annotation.Source;
4384 08 Mar 17 nicklas 28 import net.sf.basedb.core.AnnotationSet;
4381 07 Mar 17 nicklas 29 import net.sf.basedb.core.AnnotationType;
4381 07 Mar 17 nicklas 30 import net.sf.basedb.core.AnnotationTypeCategory;
4413 21 Mar 17 nicklas 31 import net.sf.basedb.core.AnyToAny;
4368 28 Feb 17 nicklas 32 import net.sf.basedb.core.BasicItem;
4370 28 Feb 17 nicklas 33 import net.sf.basedb.core.BioMaterial;
4397 13 Mar 17 nicklas 34 import net.sf.basedb.core.BioMaterialEvent;
4372 02 Mar 17 nicklas 35 import net.sf.basedb.core.BioSource;
5556 13 Aug 19 nicklas 36 import net.sf.basedb.core.BooleanParameterType;
4411 21 Mar 17 nicklas 37 import net.sf.basedb.core.DataFileType;
4366 27 Feb 17 nicklas 38 import net.sf.basedb.core.DbControl;
4372 02 Mar 17 nicklas 39 import net.sf.basedb.core.DerivedBioAssay;
4405 20 Mar 17 nicklas 40 import net.sf.basedb.core.Directory;
4368 28 Feb 17 nicklas 41 import net.sf.basedb.core.Extract;
4405 20 Mar 17 nicklas 42 import net.sf.basedb.core.File;
4366 27 Feb 17 nicklas 43 import net.sf.basedb.core.FileServer;
4411 21 Mar 17 nicklas 44 import net.sf.basedb.core.FileStoreEnabled;
4366 27 Feb 17 nicklas 45 import net.sf.basedb.core.Include;
4372 02 Mar 17 nicklas 46 import net.sf.basedb.core.InvalidDataException;
4366 27 Feb 17 nicklas 47 import net.sf.basedb.core.Item;
4386 09 Mar 17 nicklas 48 import net.sf.basedb.core.ItemNotFoundException;
4366 27 Feb 17 nicklas 49 import net.sf.basedb.core.ItemParameterType;
4366 27 Feb 17 nicklas 50 import net.sf.basedb.core.ItemQuery;
4370 28 Feb 17 nicklas 51 import net.sf.basedb.core.ItemSubtype;
4411 21 Mar 17 nicklas 52 import net.sf.basedb.core.ItemSubtypeFileType;
4366 27 Feb 17 nicklas 53 import net.sf.basedb.core.PluginParameter;
4366 27 Feb 17 nicklas 54 import net.sf.basedb.core.ProgressReporter;
4368 28 Feb 17 nicklas 55 import net.sf.basedb.core.Project;
5194 14 Dec 18 nicklas 56 import net.sf.basedb.core.ProjectKey;
4368 28 Feb 17 nicklas 57 import net.sf.basedb.core.RawBioAssay;
4372 02 Mar 17 nicklas 58 import net.sf.basedb.core.RawDataType;
4372 02 Mar 17 nicklas 59 import net.sf.basedb.core.RawDataTypes;
4366 27 Feb 17 nicklas 60 import net.sf.basedb.core.RequestInformation;
4368 28 Feb 17 nicklas 61 import net.sf.basedb.core.Sample;
4411 21 Mar 17 nicklas 62 import net.sf.basedb.core.Shareable;
4368 28 Feb 17 nicklas 63 import net.sf.basedb.core.SharedItem;
4366 27 Feb 17 nicklas 64 import net.sf.basedb.core.StringParameterType;
4370 28 Feb 17 nicklas 65 import net.sf.basedb.core.Subtypable;
4372 02 Mar 17 nicklas 66 import net.sf.basedb.core.Type;
4463 21 Apr 17 nicklas 67 import net.sf.basedb.core.Unit;
4366 27 Feb 17 nicklas 68 import net.sf.basedb.core.Job.ExecutionTime;
4368 28 Feb 17 nicklas 69 import net.sf.basedb.core.MultiPermissions;
4368 28 Feb 17 nicklas 70 import net.sf.basedb.core.Nameable;
4405 20 Mar 17 nicklas 71 import net.sf.basedb.core.Path;
4486 09 May 17 nicklas 72 import net.sf.basedb.core.PathParameterType;
4368 28 Feb 17 nicklas 73 import net.sf.basedb.core.Permission;
4387 09 Mar 17 nicklas 74 import net.sf.basedb.core.PermissionDeniedException;
4411 21 Mar 17 nicklas 75 import net.sf.basedb.core.Platform;
4411 21 Mar 17 nicklas 76 import net.sf.basedb.core.PlatformFileType;
4372 02 Mar 17 nicklas 77 import net.sf.basedb.core.PlatformVariant;
4366 27 Feb 17 nicklas 78 import net.sf.basedb.core.plugin.AbstractPlugin;
4366 27 Feb 17 nicklas 79 import net.sf.basedb.core.plugin.GuiContext;
4366 27 Feb 17 nicklas 80 import net.sf.basedb.core.plugin.InteractivePlugin;
4366 27 Feb 17 nicklas 81 import net.sf.basedb.core.plugin.Plugin;
4366 27 Feb 17 nicklas 82 import net.sf.basedb.core.plugin.Request;
4366 27 Feb 17 nicklas 83 import net.sf.basedb.core.plugin.Response;
4366 27 Feb 17 nicklas 84 import net.sf.basedb.core.query.Expressions;
4366 27 Feb 17 nicklas 85 import net.sf.basedb.core.query.Hql;
5189 12 Dec 18 nicklas 86 import net.sf.basedb.core.query.Orders;
4366 27 Feb 17 nicklas 87 import net.sf.basedb.core.query.Restrictions;
4440 03 Apr 17 nicklas 88 import net.sf.basedb.core.signal.EnhancedThreadSignalHandler;
4440 03 Apr 17 nicklas 89 import net.sf.basedb.core.signal.Signal;
4366 27 Feb 17 nicklas 90 import net.sf.basedb.core.signal.SignalException;
4366 27 Feb 17 nicklas 91 import net.sf.basedb.core.signal.SignalHandler;
4366 27 Feb 17 nicklas 92 import net.sf.basedb.core.signal.SignalTarget;
4366 27 Feb 17 nicklas 93 import net.sf.basedb.core.signal.ThreadSignalHandler;
4366 27 Feb 17 nicklas 94 import net.sf.basedb.opengrid.CmdResult;
4366 27 Feb 17 nicklas 95 import net.sf.basedb.opengrid.OpenGrid;
4366 27 Feb 17 nicklas 96 import net.sf.basedb.opengrid.RemoteHost;
4366 27 Feb 17 nicklas 97 import net.sf.basedb.opengrid.RemoteSession;
4366 27 Feb 17 nicklas 98 import net.sf.basedb.opengrid.ScriptBuilder;
4366 27 Feb 17 nicklas 99 import net.sf.basedb.opengrid.config.ConnectionInfo;
4368 28 Feb 17 nicklas 100 import net.sf.basedb.opengrid.filetransfer.ByteArrayDownloadTarget;
4405 20 Mar 17 nicklas 101 import net.sf.basedb.relax.Relax;
4608 03 Oct 17 nicklas 102 import net.sf.basedb.relax.ServerMode;
4395 13 Mar 17 nicklas 103 import net.sf.basedb.relax.converter.StringToDateConverter;
5178 05 Dec 18 nicklas 104 import net.sf.basedb.relax.dao.Annotationtype;
4433 29 Mar 17 nicklas 105 import net.sf.basedb.relax.dao.Fileserver;
4366 27 Feb 17 nicklas 106 import net.sf.basedb.util.FileUtil;
4433 29 Mar 17 nicklas 107 import net.sf.basedb.util.NameableComparator;
4368 28 Feb 17 nicklas 108 import net.sf.basedb.util.Values;
4463 21 Apr 17 nicklas 109 import net.sf.basedb.util.units.UnitUtil;
4405 20 Mar 17 nicklas 110 import net.sf.basedb.util.uri.UriMetadata;
4366 27 Feb 17 nicklas 111
4366 27 Feb 17 nicklas 112 public class ReleaseImporterPlugin 
4366 27 Feb 17 nicklas 113   extends AbstractPlugin
4366 27 Feb 17 nicklas 114   implements Plugin, InteractivePlugin, SignalTarget
4366 27 Feb 17 nicklas 115 {
4366 27 Feb 17 nicklas 116
4366 27 Feb 17 nicklas 117   
4366 27 Feb 17 nicklas 118   private RequestInformation configureImport;
4440 03 Apr 17 nicklas 119   private EnhancedThreadSignalHandler signalHandler;
4366 27 Feb 17 nicklas 120
4366 27 Feb 17 nicklas 121   
4366 27 Feb 17 nicklas 122   public ReleaseImporterPlugin()
4366 27 Feb 17 nicklas 123   {}
4366 27 Feb 17 nicklas 124   
4366 27 Feb 17 nicklas 125   @Override
4366 27 Feb 17 nicklas 126   public boolean supportsConfigurations()
4366 27 Feb 17 nicklas 127   {
4366 27 Feb 17 nicklas 128     return false;
4366 27 Feb 17 nicklas 129   }
4366 27 Feb 17 nicklas 130   @Override
4366 27 Feb 17 nicklas 131   public boolean requiresConfiguration()
4366 27 Feb 17 nicklas 132   {
4366 27 Feb 17 nicklas 133     return false;
4366 27 Feb 17 nicklas 134   }
4366 27 Feb 17 nicklas 135
4366 27 Feb 17 nicklas 136   @Override
4366 27 Feb 17 nicklas 137   public MainType getMainType()
4366 27 Feb 17 nicklas 138   {
4366 27 Feb 17 nicklas 139     return MainType.IMPORT;
4366 27 Feb 17 nicklas 140   }
4366 27 Feb 17 nicklas 141
4366 27 Feb 17 nicklas 142   @Override
4366 27 Feb 17 nicklas 143   public Set<GuiContext> getGuiContexts() 
4366 27 Feb 17 nicklas 144   {
5191 12 Dec 18 nicklas 145     return Collections.singleton(GuiContext.item(Item.PROJECT));
4366 27 Feb 17 nicklas 146   }
4366 27 Feb 17 nicklas 147
4366 27 Feb 17 nicklas 148   @Override
4366 27 Feb 17 nicklas 149   public String isInContext(GuiContext context, Object item) 
4366 27 Feb 17 nicklas 150   {
5191 12 Dec 18 nicklas 151     if (!(item instanceof Project))
5191 12 Dec 18 nicklas 152     {
5191 12 Dec 18 nicklas 153       return "This plug-in need a project item";
5191 12 Dec 18 nicklas 154     }
5191 12 Dec 18 nicklas 155     Project p = (Project)item;
5191 12 Dec 18 nicklas 156     if (sc.getActiveProjectId() != p.getId())
5191 12 Dec 18 nicklas 157     {
5191 12 Dec 18 nicklas 158       throw new PermissionDeniedException("This project is not the active project!");
5191 12 Dec 18 nicklas 159     }
5191 12 Dec 18 nicklas 160     return null;
4366 27 Feb 17 nicklas 161   }
4366 27 Feb 17 nicklas 162
4366 27 Feb 17 nicklas 163   @Override
4366 27 Feb 17 nicklas 164   public RequestInformation getRequestInformation(GuiContext context, String command)
4366 27 Feb 17 nicklas 165   {
4366 27 Feb 17 nicklas 166     RequestInformation requestInformation = null;
4366 27 Feb 17 nicklas 167     if (Request.COMMAND_CONFIGURE_JOB.equals(command))
4366 27 Feb 17 nicklas 168     {
4387 09 Mar 17 nicklas 169       if (sc.getActiveProjectId() == 0)
4387 09 Mar 17 nicklas 170       {
4387 09 Mar 17 nicklas 171         throw new PermissionDeniedException("This plug-in requires an active project!");
4387 09 Mar 17 nicklas 172       }
4366 27 Feb 17 nicklas 173       requestInformation = getConfigureImportParameters();
4366 27 Feb 17 nicklas 174     }
4366 27 Feb 17 nicklas 175     return requestInformation;
4366 27 Feb 17 nicklas 176   }
4366 27 Feb 17 nicklas 177
5190 12 Dec 18 nicklas 178   @SuppressWarnings("unchecked")
4366 27 Feb 17 nicklas 179   @Override
4366 27 Feb 17 nicklas 180   public void configure(GuiContext context, Request request, Response response) 
4366 27 Feb 17 nicklas 181   {
4366 27 Feb 17 nicklas 182     String command = request.getCommand();    
4366 27 Feb 17 nicklas 183     try
4366 27 Feb 17 nicklas 184     {
4366 27 Feb 17 nicklas 185       if (command.equals(Request.COMMAND_CONFIGURE_JOB))
4366 27 Feb 17 nicklas 186       {
4366 27 Feb 17 nicklas 187         RequestInformation ri = getConfigureImportParameters();
4366 27 Feb 17 nicklas 188         List<Throwable> errors = validateRequestParameters(ri.getParameters(), request);
4366 27 Feb 17 nicklas 189         if (errors != null)
4366 27 Feb 17 nicklas 190         {
4366 27 Feb 17 nicklas 191           response.setError(errors.size() + " invalid parameters were found in the request",errors);
4366 27 Feb 17 nicklas 192           return;
4366 27 Feb 17 nicklas 193         }
4366 27 Feb 17 nicklas 194         
4429 28 Mar 17 nicklas 195         String releaseVersion = (String)request.getParameterValue("releaseVersion");
5190 12 Dec 18 nicklas 196         String dataVersion = (String)request.getParameterValue("dataVersion");
5190 12 Dec 18 nicklas 197         if (dataVersion == null) dataVersion = releaseVersion;
5190 12 Dec 18 nicklas 198         String storedDataVersion = (String)job.getValue("dataVersion");
5190 12 Dec 18 nicklas 199
4429 28 Mar 17 nicklas 200         if (!releaseVersion.matches("\\d+\\.\\d+"))
4387 09 Mar 17 nicklas 201         {
4429 28 Mar 17 nicklas 202           response.setError("Release version must match &lt;<i>major.minor</i>&gt; where " +
4429 28 Mar 17 nicklas 203               "<i>major</i> and <i>minor</i> are numeric values: " + releaseVersion, null);
4387 09 Mar 17 nicklas 204           return;
4387 09 Mar 17 nicklas 205         }
5190 12 Dec 18 nicklas 206         if (!dataVersion.matches("\\d+\\.\\d+"))
5190 12 Dec 18 nicklas 207         {
5190 12 Dec 18 nicklas 208           response.setError("Data version must match &lt;<i>major.minor</i>&gt; where " +
5190 12 Dec 18 nicklas 209               "<i>major</i> and <i>minor</i> are numeric values: " + dataVersion, null);
5190 12 Dec 18 nicklas 210           return;
5190 12 Dec 18 nicklas 211         }
4366 27 Feb 17 nicklas 212         FileServer server = (FileServer)request.getParameterValue("fileServer");
4387 09 Mar 17 nicklas 213         
4391 10 Mar 17 nicklas 214         DbControl dc = null;
4366 27 Feb 17 nicklas 215         RemoteSession session = null;
4429 28 Mar 17 nicklas 216         JSONObject jsonList = null;
4366 27 Feb 17 nicklas 217         try
4366 27 Feb 17 nicklas 218         {
4366 27 Feb 17 nicklas 219           // Make some pre-checks before accepting the job
4366 27 Feb 17 nicklas 220           dc = sc.newDbControl();
4366 27 Feb 17 nicklas 221           server = FileServer.getById(dc, server.getId());
5190 12 Dec 18 nicklas 222           String path = server.getRootPath() + "/" + dataVersion;
4366 27 Feb 17 nicklas 223           
4366 27 Feb 17 nicklas 224           ConnectionInfo ci = new ConnectionInfo(server);
4366 27 Feb 17 nicklas 225           RemoteHost host = new RemoteHost(ci);
4366 27 Feb 17 nicklas 226           session = host.connect(5);
4366 27 Feb 17 nicklas 227           
4366 27 Feb 17 nicklas 228           ScriptBuilder script = new ScriptBuilder();
4366 27 Feb 17 nicklas 229           script.cmd("DATA_FOLDER="+path);
4366 27 Feb 17 nicklas 230           script.cmd(getStatusScript());
4366 27 Feb 17 nicklas 231           
4366 27 Feb 17 nicklas 232           CmdResult<String> checkDir = session.executeCmd(script.toString(), 5);
4366 27 Feb 17 nicklas 233           if (checkDir.getExitStatus() == 99)
4366 27 Feb 17 nicklas 234           {
5236 16 Jan 19 nicklas 235             String msg = "Can't find release data in folder: " + path;
5236 16 Jan 19 nicklas 236             if (request.getParameterValue("updateFrom") != null && request.getParameterValue("dataVersion") == null)
5236 16 Jan 19 nicklas 237             {
5236 16 Jan 19 nicklas 238               msg += "<br>In UPDATE mode, the 'Data version' parameter probably need to be set.";
5236 16 Jan 19 nicklas 239             }
5236 16 Jan 19 nicklas 240             
5236 16 Jan 19 nicklas 241             response.setError(msg, null);
4366 27 Feb 17 nicklas 242             return;
4366 27 Feb 17 nicklas 243           }
4366 27 Feb 17 nicklas 244           checkDir.throwExceptionIfNonZeroExitStatus();
4429 28 Mar 17 nicklas 245           
4429 28 Mar 17 nicklas 246           JSONObject json = (JSONObject)new JSONParser().parse(checkDir.getResult());
4429 28 Mar 17 nicklas 247           JSONObject jsonIndex = (JSONObject)json.get("index");
4429 28 Mar 17 nicklas 248           jsonList = (JSONObject)jsonIndex.get("list");
4429 28 Mar 17 nicklas 249           
4366 27 Feb 17 nicklas 250         }
4366 27 Feb 17 nicklas 251         catch (RuntimeException ex)
4366 27 Feb 17 nicklas 252         {
4366 27 Feb 17 nicklas 253           response.setError(ex.getMessage(), Collections.singletonList(ex));
4366 27 Feb 17 nicklas 254           return;
4366 27 Feb 17 nicklas 255         }
4366 27 Feb 17 nicklas 256         finally
4366 27 Feb 17 nicklas 257         {
4366 27 Feb 17 nicklas 258           if (dc != null) dc.close();
4366 27 Feb 17 nicklas 259           OpenGrid.close(session);
4366 27 Feb 17 nicklas 260         }
4366 27 Feb 17 nicklas 261         
5190 12 Dec 18 nicklas 262         if (!dataVersion.equals(storedDataVersion))
4447 07 Apr 17 nicklas 263         {
4447 07 Apr 17 nicklas 264           response.setClearState();
4447 07 Apr 17 nicklas 265         }
4366 27 Feb 17 nicklas 266         storeValue(job, request, ri.getParameter("fileServer"));
4436 30 Mar 17 nicklas 267         storeValue(job, request, ri.getParameter("releaseVersion"));
5190 12 Dec 18 nicklas 268         storeValue(job, (PluginParameter<String>)ri.getParameter("dataVersion"), dataVersion);
5189 12 Dec 18 nicklas 269         storeValue(job, request, ri.getParameter("updateFrom"));
4486 09 May 17 nicklas 270         storeValue(job, request, ri.getParameter("logFile"));
5556 13 Aug 19 nicklas 271         storeValue(job, request, ri.getParameter("debugMode"));
5189 12 Dec 18 nicklas 272         
5189 12 Dec 18 nicklas 273         Project updateFrom = (Project)request.getParameterValue("updateFrom");
4486 09 May 17 nicklas 274
5189 12 Dec 18 nicklas 275         response.setSuggestedJobName("Release import from list '" + jsonList.get("name") + 
5189 12 Dec 18 nicklas 276           "'; version " + releaseVersion + (updateFrom == null ? "; full import" : "; update from " + updateFrom.getName()));
4366 27 Feb 17 nicklas 277         response.setDone("The job configuration is complete", ExecutionTime.MEDIUM);
4366 27 Feb 17 nicklas 278       }
4366 27 Feb 17 nicklas 279     }
4366 27 Feb 17 nicklas 280     catch(Throwable ex)
4366 27 Feb 17 nicklas 281     {
4366 27 Feb 17 nicklas 282       response.setError(ex.getMessage(), Arrays.asList(ex));
4366 27 Feb 17 nicklas 283     }    
4366 27 Feb 17 nicklas 284   }
4366 27 Feb 17 nicklas 285   
4366 27 Feb 17 nicklas 286   /*
4366 27 Feb 17 nicklas 287     From the SignalTarget interface
4366 27 Feb 17 nicklas 288     -------------------------------------------
4366 27 Feb 17 nicklas 289   */
4366 27 Feb 17 nicklas 290   @Override
4366 27 Feb 17 nicklas 291   public SignalHandler getSignalHandler()
4366 27 Feb 17 nicklas 292   {
4440 03 Apr 17 nicklas 293     if (signalHandler == null)
4440 03 Apr 17 nicklas 294     {
4445 06 Apr 17 nicklas 295       signalHandler = new EnhancedThreadSignalHandler(Signal.ABORT, Signal.PAUSE, Signal.SHUTDOWN);
4440 03 Apr 17 nicklas 296     }
4366 27 Feb 17 nicklas 297     return signalHandler;
4366 27 Feb 17 nicklas 298   }
4366 27 Feb 17 nicklas 299   // -------------------------------------------
4366 27 Feb 17 nicklas 300
4405 20 Mar 17 nicklas 301   private FileServer fileServer;
4440 03 Apr 17 nicklas 302   // If set, it is possible to abort the plug-in and restart at this breakpoint
4440 03 Apr 17 nicklas 303   private String breakPointCommand;
4366 27 Feb 17 nicklas 304   
4366 27 Feb 17 nicklas 305   @Override
4366 27 Feb 17 nicklas 306   public void run(Request req, Response resp, ProgressReporter progress) 
4366 27 Feb 17 nicklas 307   {
4440 03 Apr 17 nicklas 308     if (signalHandler != null) signalHandler.setWorkerThread();
4440 03 Apr 17 nicklas 309     
4387 09 Mar 17 nicklas 310     if (sc.getActiveProjectId() == 0)
4387 09 Mar 17 nicklas 311     {
4387 09 Mar 17 nicklas 312       resp.setError("This plug-in requires an active project!", null);
4387 09 Mar 17 nicklas 313       return;
4387 09 Mar 17 nicklas 314     }
4387 09 Mar 17 nicklas 315
4429 28 Mar 17 nicklas 316     String releaseVersion = (String)job.getValue("releaseVersion");
5190 12 Dec 18 nicklas 317     String dataVersion = (String)job.getValue("dataVersion");
5190 12 Dec 18 nicklas 318     if (dataVersion == null) dataVersion = releaseVersion;
4429 28 Mar 17 nicklas 319     if (!releaseVersion.matches("\\d+\\.\\d+"))
4387 09 Mar 17 nicklas 320     {
4429 28 Mar 17 nicklas 321       resp.setError("Release version must match &lt;<i>major.minor</i>&gt; where " +
4429 28 Mar 17 nicklas 322           "<i>major</i> and <i>minor</i> are numeric values: " + releaseVersion, null);
4387 09 Mar 17 nicklas 323       return;
4387 09 Mar 17 nicklas 324     }
5190 12 Dec 18 nicklas 325     if (!dataVersion.matches("\\d+\\.\\d+"))
5190 12 Dec 18 nicklas 326     {
5190 12 Dec 18 nicklas 327       resp.setError("Data version must match &lt;<i>major.minor</i>&gt; where " +
5190 12 Dec 18 nicklas 328           "<i>major</i> and <i>minor</i> are numeric values: " + dataVersion, null);
5190 12 Dec 18 nicklas 329       return;
5190 12 Dec 18 nicklas 330     }
4440 03 Apr 17 nicklas 331
4366 27 Feb 17 nicklas 332     FileServer server = (FileServer)job.getValue("fileServer");
5189 12 Dec 18 nicklas 333     Project updateFrom = (Project)job.getValue("updateFrom");
4366 27 Feb 17 nicklas 334     DbControl dc = null;
4486 09 May 17 nicklas 335     
4366 27 Feb 17 nicklas 336     try
4366 27 Feb 17 nicklas 337     {
4486 09 May 17 nicklas 338       createLogFile((String)job.getValue("logFile"));
5556 13 Aug 19 nicklas 339       boolean debugMode = Boolean.TRUE.equals(job.getValue("debugMode"));
4486 09 May 17 nicklas 340       if (!Request.COMMAND_EXECUTE.equals(req.getCommand()))
4486 09 May 17 nicklas 341       {
4486 09 May 17 nicklas 342         // We are resuming after a break
4486 09 May 17 nicklas 343         breakPointCommand = req.getCommand();
4486 09 May 17 nicklas 344         log("Resuming from: "+breakPointCommand);
4486 09 May 17 nicklas 345         
4486 09 May 17 nicklas 346         Object tmp = req.getState();
4486 09 May 17 nicklas 347         if (tmp instanceof ImportState)
4486 09 May 17 nicklas 348         {
4486 09 May 17 nicklas 349           state = (ImportState)tmp;
4486 09 May 17 nicklas 350           // Send the stored progress information to give the user a 
4486 09 May 17 nicklas 351           // good impression -- we still have to redo some of the preparation steps
4486 09 May 17 nicklas 352           state.sendProgress(progress);
4486 09 May 17 nicklas 353         }
4486 09 May 17 nicklas 354       }
4486 09 May 17 nicklas 355
4366 27 Feb 17 nicklas 356       dc = sc.newDbControl();
4405 20 Mar 17 nicklas 357       fileServer = FileServer.getById(dc, server.getId());
4486 09 May 17 nicklas 358       ConnectionInfo ci = new ConnectionInfo(fileServer);
4391 10 Mar 17 nicklas 359       RemoteHost host = new RemoteHost(ci);
4366 27 Feb 17 nicklas 360       
5190 12 Dec 18 nicklas 361       String path = fileServer.getRootPath() + "/" + dataVersion;
4486 09 May 17 nicklas 362       log("Using file server: "+ fileServer.getHost() + path);
4486 09 May 17 nicklas 363       
4391 10 Mar 17 nicklas 364       JSONObject json = waitForExportToComplete(host, path, progress);
4366 27 Feb 17 nicklas 365       
4366 27 Feb 17 nicklas 366       // Start the import
5189 12 Dec 18 nicklas 367       String message = doImport(host, path, releaseVersion, updateFrom, progress);
5556 13 Aug 19 nicklas 368       if (debugMode)
5556 13 Aug 19 nicklas 369       {
5557 13 Aug 19 nicklas 370         // DEBUG!! will make it easier to re-run a job with same settings
5556 13 Aug 19 nicklas 371         resp.setError("[DEBUG] "+message, null);
5556 13 Aug 19 nicklas 372       }
5556 13 Aug 19 nicklas 373       else
5556 13 Aug 19 nicklas 374       {
5556 13 Aug 19 nicklas 375         resp.setDone(message);
5556 13 Aug 19 nicklas 376       }
4366 27 Feb 17 nicklas 377     }
4445 06 Apr 17 nicklas 378     catch (Exception ex)
4440 03 Apr 17 nicklas 379     {
4486 09 May 17 nicklas 380       log(ex.getMessage(), ex);
4445 06 Apr 17 nicklas 381       if (signalHandler != null)
4440 03 Apr 17 nicklas 382       {
4445 06 Apr 17 nicklas 383         if (signalHandler.hasReceived(Signal.SHUTDOWN) && breakPointCommand != null)
4445 06 Apr 17 nicklas 384         {
4445 06 Apr 17 nicklas 385           resp.setContinue(breakPointCommand, state);
4445 06 Apr 17 nicklas 386           return;
4445 06 Apr 17 nicklas 387         }
4445 06 Apr 17 nicklas 388         else if (signalHandler.hasReceived(Signal.PAUSE) && breakPointCommand != null)
4445 06 Apr 17 nicklas 389         {
4445 06 Apr 17 nicklas 390           resp.setPause(null, breakPointCommand, state);
4445 06 Apr 17 nicklas 391           return;
4445 06 Apr 17 nicklas 392         }
4445 06 Apr 17 nicklas 393         else if (signalHandler.hasReceived(Signal.ABORT))
4445 06 Apr 17 nicklas 394         {
4445 06 Apr 17 nicklas 395           resp.setError("Aborted by user", Collections.singletonList(ex));
4445 06 Apr 17 nicklas 396           return;
4445 06 Apr 17 nicklas 397         }
4440 03 Apr 17 nicklas 398       }
4366 27 Feb 17 nicklas 399       resp.setError(ex.getMessage(), Collections.singletonList(ex));
4366 27 Feb 17 nicklas 400       return;
4366 27 Feb 17 nicklas 401     }
4366 27 Feb 17 nicklas 402     finally
4366 27 Feb 17 nicklas 403     {
4366 27 Feb 17 nicklas 404       if (dc != null) dc.close();
4486 09 May 17 nicklas 405       closeLogFile();
4366 27 Feb 17 nicklas 406     }
4366 27 Feb 17 nicklas 407   }
4366 27 Feb 17 nicklas 408
4366 27 Feb 17 nicklas 409   private RequestInformation getConfigureImportParameters()
4366 27 Feb 17 nicklas 410   {
4366 27 Feb 17 nicklas 411     if (configureImport == null)
4366 27 Feb 17 nicklas 412     {
4366 27 Feb 17 nicklas 413       List<PluginParameter<?>> parameters = new ArrayList<PluginParameter<?>>();
4366 27 Feb 17 nicklas 414       
4366 27 Feb 17 nicklas 415       DbControl dc = null;
4366 27 Feb 17 nicklas 416       try
4366 27 Feb 17 nicklas 417       {
4608 03 Oct 17 nicklas 418         Fileserver defaultFileServer = Relax.getServerMode() == ServerMode.RELAX ? 
4608 03 Oct 17 nicklas 419           Fileserver.RELEASE_ARCHIVE : Fileserver.LEVEL3_ARCHIVE;
4608 03 Oct 17 nicklas 420         
4366 27 Feb 17 nicklas 421         dc = sc.newDbControl();
4366 27 Feb 17 nicklas 422         
5190 12 Dec 18 nicklas 423         // The "Create release" section
5190 12 Dec 18 nicklas 424         parameters.add(new PluginParameter<String>(
5190 12 Dec 18 nicklas 425           "releaseSection", "Create release",  
5190 12 Dec 18 nicklas 426           "Options for the new release that is being created by this importer.", null));
5190 12 Dec 18 nicklas 427         
5190 12 Dec 18 nicklas 428         // The release version to create
5190 12 Dec 18 nicklas 429         Project p = Project.getById(dc, sc.getActiveProjectId());
5190 12 Dec 18 nicklas 430         Pattern getVersion = Pattern.compile("(\\d+\\.\\d+)");
5190 12 Dec 18 nicklas 431         Matcher m = getVersion.matcher(p.getName());
5190 12 Dec 18 nicklas 432         String releaseVersion = m.find() ? m.group(1) : null;
5190 12 Dec 18 nicklas 433         parameters.add(new PluginParameter<String>(
5190 12 Dec 18 nicklas 434           "releaseVersion", "Release version",
5190 12 Dec 18 nicklas 435           "The version number of the release to create. It should consist of a major and minor " +
5190 12 Dec 18 nicklas 436           "number separated with a dot. For example: 1.0, 1.2, 2.1.",
5190 12 Dec 18 nicklas 437           new StringParameterType(255, releaseVersion, true)));
5190 12 Dec 18 nicklas 438   
5247 18 Jan 19 nicklas 439         if (Relax.getServerMode() == ServerMode.RELAX)
5247 18 Jan 19 nicklas 440         {
5247 18 Jan 19 nicklas 441           ItemQuery<Project> projectQuery = Project.getQuery();
5247 18 Jan 19 nicklas 442           projectQuery.setIncludes(Include.ALL);
5247 18 Jan 19 nicklas 443           projectQuery.exclude(Include.REMOVED);
5247 18 Jan 19 nicklas 444           projectQuery.order(Orders.asc(Hql.property("name")));
5247 18 Jan 19 nicklas 445           List<Project> allProjects = projectQuery.list(dc);
5247 18 Jan 19 nicklas 446           parameters.add(new PluginParameter<Project>(
5247 18 Jan 19 nicklas 447             "updateFrom", "Update from",
5247 18 Jan 19 nicklas 448             "If a project is selected, the import will only update items that already exists in the "+
5247 18 Jan 19 nicklas 449             "selected project and no new items are created. If no project is selected, a full import "+
5247 18 Jan 19 nicklas 450             "is made.",
5247 18 Jan 19 nicklas 451             new ItemParameterType<Project>(Project.class, null, false, 1, allProjects)
5247 18 Jan 19 nicklas 452           ));
5247 18 Jan 19 nicklas 453         }
5190 12 Dec 18 nicklas 454
5190 12 Dec 18 nicklas 455         // The "Data source" section
5190 12 Dec 18 nicklas 456         parameters.add(new PluginParameter<String>(
5190 12 Dec 18 nicklas 457           "dataSection", "Load data from", 
5190 12 Dec 18 nicklas 458           "Options related to where the data for the release can be found.", null));
4366 27 Feb 17 nicklas 459         ItemQuery<FileServer> query = FileServer.getQuery();
4366 27 Feb 17 nicklas 460         query.setIncludes(Include.ALL);
5189 12 Dec 18 nicklas 461         query.exclude(Include.REMOVED);
4391 10 Mar 17 nicklas 462         query.restrict(
4391 10 Mar 17 nicklas 463           Restrictions.eq(
4391 10 Mar 17 nicklas 464             Hql.property("connectionManagerFactory"), 
4391 10 Mar 17 nicklas 465             Expressions.string("net.sf.basedb.xfiles.sftp-connection-manager")
4391 10 Mar 17 nicklas 466           ));
4433 29 Mar 17 nicklas 467         List<FileServer> fileServers = new ArrayList<FileServer>(query.list(dc));
4608 03 Oct 17 nicklas 468         FileServer releaseArchive = defaultFileServer.load(dc);
4433 29 Mar 17 nicklas 469         if (!fileServers.contains(releaseArchive)) fileServers.add(releaseArchive);
4433 29 Mar 17 nicklas 470         fileServers.sort(new NameableComparator<>(false));
4433 29 Mar 17 nicklas 471
4366 27 Feb 17 nicklas 472         parameters.add(new PluginParameter<FileServer>(
4366 27 Feb 17 nicklas 473           "fileServer", "File server",
5190 12 Dec 18 nicklas 474           "Select the file server where the data files are located.",
4433 29 Mar 17 nicklas 475           new ItemParameterType<>(FileServer.class, releaseArchive, true, 1, fileServers)
4366 27 Feb 17 nicklas 476         ));
4433 29 Mar 17 nicklas 477
4433 29 Mar 17 nicklas 478         parameters.add(new PluginParameter<String>(
5190 12 Dec 18 nicklas 479           "dataVersion", "Data version",
5190 12 Dec 18 nicklas 480           "The version number of the data to read from. If not specified, the value from the <b>Release version</b> option is used.",
5190 12 Dec 18 nicklas 481           new StringParameterType(255, null, false)));
4486 09 May 17 nicklas 482         
5190 12 Dec 18 nicklas 483         // The "Other options" section
4486 09 May 17 nicklas 484         parameters.add(new PluginParameter<String>(
5190 12 Dec 18 nicklas 485           "optionsSection", "Other options", "", null));
5190 12 Dec 18 nicklas 486         parameters.add(new PluginParameter<String>(
4486 09 May 17 nicklas 487           "logFile", "Log file",
4486 09 May 17 nicklas 488           "Specify a file path for logging detailed information about the plug-ins " +
4486 09 May 17 nicklas 489           "work. Logging will be disabled if this parameter is empty.",
4486 09 May 17 nicklas 490           new PathParameterType(Path.Type.FILE, null, false)
4486 09 May 17 nicklas 491         ));
5556 13 Aug 19 nicklas 492         
5556 13 Aug 19 nicklas 493         // If <developer-mode>1</developer-mode> is in relax-config.xml we allow debug via parameter
5556 13 Aug 19 nicklas 494         if (Values.getBoolean(Relax.getConfig().getConfig("developer-mode")))
5556 13 Aug 19 nicklas 495         {
5556 13 Aug 19 nicklas 496           parameters.add(new PluginParameter<Boolean>(
5556 13 Aug 19 nicklas 497             "debugMode", "Debug mode", "Runs the plug-in in debug mode. Major difference is "
5556 13 Aug 19 nicklas 498                 + "that it will always report an error making it easier to re-start the "
5556 13 Aug 19 nicklas 499                 + "plug-in with the same parameters.",
5556 13 Aug 19 nicklas 500             new BooleanParameterType(null, false)
5556 13 Aug 19 nicklas 501             ));
5556 13 Aug 19 nicklas 502         }
5556 13 Aug 19 nicklas 503
4366 27 Feb 17 nicklas 504       }
4366 27 Feb 17 nicklas 505       finally
4366 27 Feb 17 nicklas 506       {
4366 27 Feb 17 nicklas 507         if (dc != null) dc.close();
4366 27 Feb 17 nicklas 508       }
4366 27 Feb 17 nicklas 509       
4366 27 Feb 17 nicklas 510       configureImport = new RequestInformation
4366 27 Feb 17 nicklas 511       (
4366 27 Feb 17 nicklas 512         Request.COMMAND_CONFIGURE_JOB,
5190 12 Dec 18 nicklas 513         "Import release",
5190 12 Dec 18 nicklas 514         "Please specify information about the new release and where to find the source data for it.",
4366 27 Feb 17 nicklas 515         parameters
4366 27 Feb 17 nicklas 516       );
4366 27 Feb 17 nicklas 517     }
4366 27 Feb 17 nicklas 518     return configureImport;
4366 27 Feb 17 nicklas 519   }
4366 27 Feb 17 nicklas 520
4366 27 Feb 17 nicklas 521   private JSONObject waitForExportToComplete(RemoteHost host, String path, ProgressReporter progress)
4366 27 Feb 17 nicklas 522     throws Exception
4366 27 Feb 17 nicklas 523   {
4366 27 Feb 17 nicklas 524     RemoteSession session = null;
4366 27 Feb 17 nicklas 525     JSONObject json = null;
4366 27 Feb 17 nicklas 526     
4366 27 Feb 17 nicklas 527     try
4366 27 Feb 17 nicklas 528     {
4366 27 Feb 17 nicklas 529       // Get the script for checking export status
4366 27 Feb 17 nicklas 530       ScriptBuilder script = new ScriptBuilder();
4366 27 Feb 17 nicklas 531       script.cmd("DATA_FOLDER="+path);
4366 27 Feb 17 nicklas 532       script.cmd(getStatusScript());
4366 27 Feb 17 nicklas 533
4366 27 Feb 17 nicklas 534       boolean exportComplete = false;
4366 27 Feb 17 nicklas 535       while (!exportComplete)
4366 27 Feb 17 nicklas 536       {
4366 27 Feb 17 nicklas 537         session = host.connect(5);
4366 27 Feb 17 nicklas 538         CmdResult<String> status = session.executeCmd(script.toString(), 5);
4366 27 Feb 17 nicklas 539         OpenGrid.close(session);
4366 27 Feb 17 nicklas 540         
4366 27 Feb 17 nicklas 541         if (status.getExitStatus() == 99)
4366 27 Feb 17 nicklas 542         {
4366 27 Feb 17 nicklas 543           throw new RuntimeException("Can't find release data in folder: " + path);
4366 27 Feb 17 nicklas 544         }
4366 27 Feb 17 nicklas 545         status.throwExceptionIfNonZeroExitStatus();
4366 27 Feb 17 nicklas 546         
4366 27 Feb 17 nicklas 547         json = (JSONObject)new JSONParser().parse(status.getStdout());
4366 27 Feb 17 nicklas 548         String endDate = (String)json.get("enddate");
4366 27 Feb 17 nicklas 549         exportComplete = endDate != null;
4366 27 Feb 17 nicklas 550         
4366 27 Feb 17 nicklas 551         if (!exportComplete) 
4366 27 Feb 17 nicklas 552         {
4366 27 Feb 17 nicklas 553           Number jsonCount = (Number)json.get("jsoncount");
4366 27 Feb 17 nicklas 554           Number size = null;
4366 27 Feb 17 nicklas 555           
4366 27 Feb 17 nicklas 556           JSONObject jsonIndex = (JSONObject)json.get("index");
4366 27 Feb 17 nicklas 557           if (jsonIndex != null)
4366 27 Feb 17 nicklas 558           {
4366 27 Feb 17 nicklas 559             JSONObject jsonList = (JSONObject)jsonIndex.get("list");
4366 27 Feb 17 nicklas 560             size = (Number)jsonList.get("size");
4366 27 Feb 17 nicklas 561           }
4366 27 Feb 17 nicklas 562           
4366 27 Feb 17 nicklas 563           if (jsonCount != null && size != null)
4366 27 Feb 17 nicklas 564           {
4366 27 Feb 17 nicklas 565             progress.display(2, "Waiting for export to complete (" + jsonCount + " of " + size + " done)");
4366 27 Feb 17 nicklas 566           }
4366 27 Feb 17 nicklas 567           else
4366 27 Feb 17 nicklas 568           {
4366 27 Feb 17 nicklas 569             progress.display(1, "Waiting for export to complete");
4366 27 Feb 17 nicklas 570           }
4366 27 Feb 17 nicklas 571           Thread.sleep(60000);
4366 27 Feb 17 nicklas 572         }
4366 27 Feb 17 nicklas 573       }
4366 27 Feb 17 nicklas 574     }
4366 27 Feb 17 nicklas 575     catch (InterruptedException ex)
4366 27 Feb 17 nicklas 576     {
4366 27 Feb 17 nicklas 577       throw new SignalException("Aborted by user");
4366 27 Feb 17 nicklas 578     }
4366 27 Feb 17 nicklas 579     finally
4366 27 Feb 17 nicklas 580     {
4366 27 Feb 17 nicklas 581       OpenGrid.close(session);
4366 27 Feb 17 nicklas 582     }
4366 27 Feb 17 nicklas 583     
4366 27 Feb 17 nicklas 584     return json;
4366 27 Feb 17 nicklas 585   }
4366 27 Feb 17 nicklas 586   
4366 27 Feb 17 nicklas 587   private String getStatusScript()
4366 27 Feb 17 nicklas 588   {
4366 27 Feb 17 nicklas 589     String script = null;
4366 27 Feb 17 nicklas 590     InputStream in = null;
4366 27 Feb 17 nicklas 591     ByteArrayOutputStream buffer = new ByteArrayOutputStream(2048);
4366 27 Feb 17 nicklas 592     try
4366 27 Feb 17 nicklas 593     {
4366 27 Feb 17 nicklas 594       in = getClass().getResourceAsStream("/net/sf/basedb/relax/bash/check_export_status.sh");
4366 27 Feb 17 nicklas 595       FileUtil.copy(in, buffer);
4366 27 Feb 17 nicklas 596       script = buffer.toString("UTF-8");
4366 27 Feb 17 nicklas 597     }
4366 27 Feb 17 nicklas 598     catch (IOException ex)
4366 27 Feb 17 nicklas 599     {
4366 27 Feb 17 nicklas 600       throw new RuntimeException(ex);
4366 27 Feb 17 nicklas 601     }
4366 27 Feb 17 nicklas 602     finally
4366 27 Feb 17 nicklas 603     {
4366 27 Feb 17 nicklas 604       FileUtil.close(in);
4366 27 Feb 17 nicklas 605     }
4366 27 Feb 17 nicklas 606     return script;
4366 27 Feb 17 nicklas 607   }
4366 27 Feb 17 nicklas 608   
4403 17 Mar 17 nicklas 609   private Project currentProject;
4372 02 Mar 17 nicklas 610   private Map<Item, ItemImporter<?>> importers;
4411 21 Mar 17 nicklas 611   // Maps for Name -> Item
4372 02 Mar 17 nicklas 612   private Map<String, ItemSubtype> subtypes;
4411 21 Mar 17 nicklas 613   private Map<String, AnnotationTypeCategory> categories;
6784 02 Aug 22 nicklas 614   private Map<String, ImportedAnnotationType> annotationTypes;
4467 25 Apr 17 nicklas 615   private Map<String, Map<String, ?>> lookups;
4411 21 Mar 17 nicklas 616   private Map<String, DataFileType> fileTypes;
4386 09 Mar 17 nicklas 617   private Map<String, BasicItem> namedItems;
4372 02 Mar 17 nicklas 618   
6784 02 Aug 22 nicklas 619   private ImportedAnnotationType dataFilesFolder;
5178 05 Dec 18 nicklas 620   private AnnotationType firstReleasedIn;
4403 17 Mar 17 nicklas 621   private String releaseVersion;
4403 17 Mar 17 nicklas 622   
4395 13 Mar 17 nicklas 623   private StringToDateConverter dateFormat;
4395 13 Mar 17 nicklas 624   private StringToDateConverter dateTimeFormat;
4405 20 Mar 17 nicklas 625   private StringToDateConverter timeStampFormat;
4386 09 Mar 17 nicklas 626
4445 06 Apr 17 nicklas 627   // State information
4445 06 Apr 17 nicklas 628   private ImportState state;
4386 09 Mar 17 nicklas 629   
4467 25 Apr 17 nicklas 630   @SuppressWarnings("unchecked")
5189 12 Dec 18 nicklas 631   private String doImport(RemoteHost host, String path, String releaseVersion, Project updateFrom, ProgressReporter progress)
4368 28 Feb 17 nicklas 632     throws Exception
4368 28 Feb 17 nicklas 633   {
4429 28 Mar 17 nicklas 634     this.releaseVersion = releaseVersion;
4429 28 Mar 17 nicklas 635     
4368 28 Feb 17 nicklas 636     RemoteSession session = null;
4372 02 Mar 17 nicklas 637     DbControl dc = null;
4368 28 Feb 17 nicklas 638   
4372 02 Mar 17 nicklas 639     subtypes = new HashMap<>();
4411 21 Mar 17 nicklas 640     categories = new HashMap<>();
4384 08 Mar 17 nicklas 641     annotationTypes = new HashMap<>();
4467 25 Apr 17 nicklas 642     lookups = new HashMap<>();
4411 21 Mar 17 nicklas 643     fileTypes = new HashMap<>();
4386 09 Mar 17 nicklas 644     namedItems = new HashMap<>();
7036 10 Feb 23 nicklas 645     dateFormat = new StringToDateConverter(FastDateFormat.getInstance("yyyyMMdd"));
7036 10 Feb 23 nicklas 646     dateTimeFormat = new StringToDateConverter(FastDateFormat.getInstance("yyyyMMdd HHmm"));
7036 10 Feb 23 nicklas 647     timeStampFormat = new StringToDateConverter(FastDateFormat.getInstance("yyyyMMdd HHmmss"));
4372 02 Mar 17 nicklas 648     
5189 12 Dec 18 nicklas 649     
4386 09 Mar 17 nicklas 650     importers = new HashMap<>();
5189 12 Dec 18 nicklas 651     importers.put(Item.BIOSOURCE, new BioSourceImporter(updateFrom, namedItems));
5189 12 Dec 18 nicklas 652     importers.put(Item.SAMPLE, new SampleImporter(updateFrom, namedItems));
5189 12 Dec 18 nicklas 653     importers.put(Item.EXTRACT, new ExtractImporter(updateFrom, namedItems));
5189 12 Dec 18 nicklas 654     importers.put(Item.DERIVEDBIOASSAY, new DerivedBioAssayImporter(updateFrom, namedItems));
5189 12 Dec 18 nicklas 655     importers.put(Item.RAWBIOASSAY, new RawBioAssayImporter(updateFrom, namedItems));
4403 17 Mar 17 nicklas 656     
4447 07 Apr 17 nicklas 657     if (state == null) 
4447 07 Apr 17 nicklas 658     {
4447 07 Apr 17 nicklas 659       state = new ImportState();
4447 07 Apr 17 nicklas 660       state.percent = 10f;
4447 07 Apr 17 nicklas 661       state.firstFileNo = 0;
4447 07 Apr 17 nicklas 662     }
4441 03 Apr 17 nicklas 663     
4386 09 Mar 17 nicklas 664     String currentFile = null;
4368 28 Feb 17 nicklas 665     try
4368 28 Feb 17 nicklas 666     {
4383 07 Mar 17 nicklas 667       JSONParser parser = new JSONParser();
4383 07 Mar 17 nicklas 668       
4372 02 Mar 17 nicklas 669       dc = sc.newDbControl();
4403 17 Mar 17 nicklas 670       currentProject = Project.getById(dc, sc.getActiveProjectId());
4403 17 Mar 17 nicklas 671       
5189 12 Dec 18 nicklas 672       log("Import mode: " + (updateFrom == null ? "Full" : "Update from project '" + updateFrom.getName() + "'"));
5189 12 Dec 18 nicklas 673       
5190 12 Dec 18 nicklas 674       if (updateFrom == null)
5178 05 Dec 18 nicklas 675       {
5190 12 Dec 18 nicklas 676         // New items may be created...
5190 12 Dec 18 nicklas 677         // Add releaseVersion to FirstRelease annotation type
5190 12 Dec 18 nicklas 678         firstReleasedIn = Annotationtype.FIRST_RELEASED_IN.load(dc);
5190 12 Dec 18 nicklas 679         dc.initCollection(firstReleasedIn, "itemTypes");
5190 12 Dec 18 nicklas 680         dc.initCollection(firstReleasedIn, "options");
5507 18 Jun 19 nicklas 681         List<String> allReleases = firstReleasedIn.getValues();
5190 12 Dec 18 nicklas 682         if (!allReleases.contains(releaseVersion))
5190 12 Dec 18 nicklas 683         {
5190 12 Dec 18 nicklas 684           log("Adding option to FirstReleasedIn: " + releaseVersion);
5190 12 Dec 18 nicklas 685           allReleases = new ArrayList<>(allReleases); // Need copy since the list is locked for modification
5190 12 Dec 18 nicklas 686           allReleases.add(releaseVersion);
5190 12 Dec 18 nicklas 687           firstReleasedIn.setValues(allReleases);
5190 12 Dec 18 nicklas 688         }
5178 05 Dec 18 nicklas 689       }
5178 05 Dec 18 nicklas 690       
4411 21 Mar 17 nicklas 691       // Load existing subtypes
4372 02 Mar 17 nicklas 692       ItemQuery<ItemSubtype> subtypeQuery = ItemSubtype.getQuery(null);
4411 21 Mar 17 nicklas 693       subtypeQuery.setIncludes(Include.ALL);
4372 02 Mar 17 nicklas 694       for (ItemSubtype subtype : subtypeQuery.list(dc))
4372 02 Mar 17 nicklas 695       {
4372 02 Mar 17 nicklas 696         subtypes.put(subtype.getName(), subtype);
4372 02 Mar 17 nicklas 697         dc.initCollection(subtype, "relatedSubtypes");
4372 02 Mar 17 nicklas 698       }
4368 28 Feb 17 nicklas 699       
4411 21 Mar 17 nicklas 700       // Load existing annotation type categories
4411 21 Mar 17 nicklas 701       ItemQuery<AnnotationTypeCategory> categoryQuery = AnnotationTypeCategory.getQuery();
4411 21 Mar 17 nicklas 702       categoryQuery.setIncludes(Include.ALL);
4411 21 Mar 17 nicklas 703       for (AnnotationTypeCategory cat : categoryQuery.list(dc))
4411 21 Mar 17 nicklas 704       {
4411 21 Mar 17 nicklas 705         categories.put(cat.getName(), cat);
4411 21 Mar 17 nicklas 706       }
4411 21 Mar 17 nicklas 707
4368 28 Feb 17 nicklas 708       session = host.connect(5);
4368 28 Feb 17 nicklas 709
4386 09 Mar 17 nicklas 710       // Download annotationtypes.json and import annotation types
4411 21 Mar 17 nicklas 711       currentFile = "/typedefs.json";
4486 09 May 17 nicklas 712       log("Processing file: "+currentFile);
4386 09 Mar 17 nicklas 713       ByteArrayDownloadTarget data = new ByteArrayDownloadTarget(currentFile);
4386 09 Mar 17 nicklas 714       session.downloadFile(path + currentFile, data);
4411 21 Mar 17 nicklas 715       JSONArray jsonTypeDefs = (JSONArray)parser.parse(data.getString("UTF-8"));
4411 21 Mar 17 nicklas 716       importTypeDefs(dc, jsonTypeDefs);
4411 21 Mar 17 nicklas 717       dc.commit();
4411 21 Mar 17 nicklas 718       
4403 17 Mar 17 nicklas 719       dataFilesFolder = annotationTypes.get("DataFilesFolder");
4403 17 Mar 17 nicklas 720       if (dataFilesFolder == null)
4403 17 Mar 17 nicklas 721       {
4403 17 Mar 17 nicklas 722         throw new ItemNotFoundException("AnnotationType[DataFilesFolder]");
4403 17 Mar 17 nicklas 723       }
4403 17 Mar 17 nicklas 724       
4467 25 Apr 17 nicklas 725       // Load batch index lookup file
4467 25 Apr 17 nicklas 726       currentFile = "/batch-index-lookup.json";
4486 09 May 17 nicklas 727       log("Processing file: "+currentFile);
4467 25 Apr 17 nicklas 728       data = new ByteArrayDownloadTarget(currentFile);
4467 25 Apr 17 nicklas 729       session.downloadFile(path + currentFile, data);
4467 25 Apr 17 nicklas 730       lookups = (Map<String, Map<String, ?>>)parser.parse(data.getString("UTF-8"));
4486 09 May 17 nicklas 731       for (Map.Entry<String, Map<String, ?>> entry : lookups.entrySet())
4486 09 May 17 nicklas 732       {
4486 09 May 17 nicklas 733         log("  Batch index lookup '" + entry.getKey() + "': " + entry.getValue().size() + " entries");
4486 09 May 17 nicklas 734       }
4467 25 Apr 17 nicklas 735       
4383 07 Mar 17 nicklas 736       // Get all files with cohort data
4386 09 Mar 17 nicklas 737       currentFile = "/files.json";
4486 09 May 17 nicklas 738       log("Processing file: "+currentFile);
4386 09 Mar 17 nicklas 739       data = new ByteArrayDownloadTarget(currentFile);
4386 09 Mar 17 nicklas 740       session.downloadFile(path + currentFile, data);
4383 07 Mar 17 nicklas 741       JSONArray jsonFiles = (JSONArray)parser.parse(data.getString("UTF-8"));
4445 06 Apr 17 nicklas 742       state.numFiles = jsonFiles.size();
4486 09 May 17 nicklas 743       log("  Found " + state.numFiles + " JSON files");
4386 09 Mar 17 nicklas 744       currentFile = null;
4381 07 Mar 17 nicklas 745       
4445 06 Apr 17 nicklas 746       float percentPerFile = 80f / state.numFiles;
4447 07 Apr 17 nicklas 747       for (int fileNo = state.firstFileNo; fileNo < state.numFiles; fileNo++)
4368 28 Feb 17 nicklas 748       {
4368 28 Feb 17 nicklas 749         ThreadSignalHandler.checkInterrupted();
4440 03 Apr 17 nicklas 750         currentFile = (String)jsonFiles.get(fileNo);
4486 09 May 17 nicklas 751         log("Processing file: "+currentFile);
4368 28 Feb 17 nicklas 752         
4447 07 Apr 17 nicklas 753         state.percent += percentPerFile;
4447 07 Apr 17 nicklas 754         state.message = "Importing " + currentFile + 
4447 07 Apr 17 nicklas 755           " (" + (fileNo+1) + " of " + state.numFiles + "; " + Values.formatBytes(state.totalSize) + ")";
4447 07 Apr 17 nicklas 756         state.sendProgress(progress);
4445 06 Apr 17 nicklas 757           
4447 07 Apr 17 nicklas 758         breakPointCommand = currentFile;
4447 07 Apr 17 nicklas 759         data = new ByteArrayDownloadTarget(currentFile);
4447 07 Apr 17 nicklas 760         session.downloadFile(path + currentFile, data);
4447 07 Apr 17 nicklas 761         state.totalSize += data.getDownloadedSize();
4440 03 Apr 17 nicklas 762           
4447 07 Apr 17 nicklas 763         JSONArray jsonItems = (JSONArray)parser.parse(data.getString("UTF-8"));
5189 12 Dec 18 nicklas 764         importCohortItems(jsonItems, currentFile, updateFrom);
4447 07 Apr 17 nicklas 765         state.firstFileNo = fileNo+1;
4386 09 Mar 17 nicklas 766         currentFile = null;
4368 28 Feb 17 nicklas 767       }
4368 28 Feb 17 nicklas 768     }
4440 03 Apr 17 nicklas 769     catch (SignalException ex)
4440 03 Apr 17 nicklas 770     {
4440 03 Apr 17 nicklas 771       // Handled in run() method
4440 03 Apr 17 nicklas 772       throw ex;
4440 03 Apr 17 nicklas 773     }
4386 09 Mar 17 nicklas 774     catch (RuntimeException ex)
4386 09 Mar 17 nicklas 775     {
4386 09 Mar 17 nicklas 776       if (currentFile == null) throw ex;
4386 09 Mar 17 nicklas 777       throw new RuntimeException("Import of '" + currentFile + "' failed: " + ex.getMessage(), ex);
4386 09 Mar 17 nicklas 778     }
4368 28 Feb 17 nicklas 779     finally
4368 28 Feb 17 nicklas 780     {
4393 10 Mar 17 nicklas 781       OpenGrid.close(session);
4372 02 Mar 17 nicklas 782       if (dc != null) dc.close();
4368 28 Feb 17 nicklas 783     }
4441 03 Apr 17 nicklas 784     
5189 12 Dec 18 nicklas 785     String msg = null;
5189 12 Dec 18 nicklas 786     if (updateFrom != null)
5189 12 Dec 18 nicklas 787     {
5189 12 Dec 18 nicklas 788       msg = "Update from " + updateFrom.getName() + ", " + 
5189 12 Dec 18 nicklas 789         state.numFiles + " files processed, " +
5189 12 Dec 18 nicklas 790         (state.numItems-state.numNotFound) + " existing items; " +
5189 12 Dec 18 nicklas 791         state.numNotFound +" not found, " + 
6784 02 Aug 22 nicklas 792         state.numAnnotations + " annotations created or updated, " +
6784 02 Aug 22 nicklas 793         state.numDefaultConverted + " default values converted";
5189 12 Dec 18 nicklas 794     }
5189 12 Dec 18 nicklas 795     else
5189 12 Dec 18 nicklas 796     {
5189 12 Dec 18 nicklas 797       msg = "Full import: " + state.numFiles + " files processed, " +
5189 12 Dec 18 nicklas 798         state.numItems + " total items, " + 
5189 12 Dec 18 nicklas 799         state.numItemsCreated + " new items, " + 
6784 02 Aug 22 nicklas 800         state.numAnnotations + " annotations created or updated, " +
6784 02 Aug 22 nicklas 801         state.numDefaultConverted + " default values converted";
5189 12 Dec 18 nicklas 802     }
4441 03 Apr 17 nicklas 803     return msg;
4368 28 Feb 17 nicklas 804   }
4368 28 Feb 17 nicklas 805
4411 21 Mar 17 nicklas 806   private void importTypeDefs(DbControl dc, JSONArray jsonTypeDefs)
4381 07 Mar 17 nicklas 807   {
4411 21 Mar 17 nicklas 808     List<SharedItem> itemsToShare = new ArrayList<>();
4411 21 Mar 17 nicklas 809     
6784 02 Aug 22 nicklas 810     AnnotationTypeImporter annotationTypeImporter = new AnnotationTypeImporter(categories, itemsToShare);
4411 21 Mar 17 nicklas 811     DataFileTypeImporter fileTypeImporter = new DataFileTypeImporter(fileTypes, subtypes);
4411 21 Mar 17 nicklas 812     
4411 21 Mar 17 nicklas 813     for (int defNo = 0; defNo < jsonTypeDefs.size(); defNo++)
4381 07 Mar 17 nicklas 814     {
4411 21 Mar 17 nicklas 815       JSONObject json = (JSONObject)jsonTypeDefs.get(defNo);
4411 21 Mar 17 nicklas 816       Item itemType = Item.valueOf((String)json.get("type"));
4411 21 Mar 17 nicklas 817       String name = (String)json.get("name");
4381 07 Mar 17 nicklas 818       
4411 21 Mar 17 nicklas 819       if (itemType == Item.ANNOTATIONTYPE)
4411 21 Mar 17 nicklas 820       {          
4381 07 Mar 17 nicklas 821         // Find or create
7012 26 Jan 23 nicklas 822         ImportedAnnotationType existing = annotationTypes.get(name);
7012 26 Jan 23 nicklas 823         if (existing != null)
7012 26 Jan 23 nicklas 824         {
7012 26 Jan 23 nicklas 825           AnnotationType at = existing.annotationType;
7012 26 Jan 23 nicklas 826           annotationTypeImporter.update(dc, at, name, json);
7012 26 Jan 23 nicklas 827           log(at.isInDatabase() ? "  Existing annotation type: " + name : "  Updated new annotation type: "+ name);
7012 26 Jan 23 nicklas 828         }
7012 26 Jan 23 nicklas 829         else
7012 26 Jan 23 nicklas 830         {
7012 26 Jan 23 nicklas 831           AnnotationType at = annotationTypeImporter.findOrCreateItem(dc, name, json);
7012 26 Jan 23 nicklas 832           log(at.isInDatabase() ? "  Existing annotation type: " + name : "  Created annotation type: "+ name);
7012 26 Jan 23 nicklas 833           annotationTypes.put(name, new ImportedAnnotationType(at));
7012 26 Jan 23 nicklas 834           dc.initCollection(at, "options");
7012 26 Jan 23 nicklas 835           // Permissions
7012 26 Jan 23 nicklas 836           itemsToShare.add(at);
7012 26 Jan 23 nicklas 837         }
4381 07 Mar 17 nicklas 838       }
4411 21 Mar 17 nicklas 839       else if (itemType == Item.DATAFILETYPE)
4381 07 Mar 17 nicklas 840       {
4411 21 Mar 17 nicklas 841         DataFileType dft = fileTypeImporter.findOrCreateItem(dc, name, json);
4486 09 May 17 nicklas 842         log(dft.isInDatabase() ? "  Existing file type: " + name : "  Created file type: "+ name);
4411 21 Mar 17 nicklas 843         fileTypes.put(name, dft);
4381 07 Mar 17 nicklas 844       }
4381 07 Mar 17 nicklas 845     }
4411 21 Mar 17 nicklas 846     
4411 21 Mar 17 nicklas 847     if (sc.getActiveProjectId() != 0)
4381 07 Mar 17 nicklas 848     {
4411 21 Mar 17 nicklas 849       MultiPermissions mp = new MultiPermissions(itemsToShare);
4411 21 Mar 17 nicklas 850       mp.addPermissions(currentProject, EnumSet.of(Permission.READ));
4411 21 Mar 17 nicklas 851       mp.updateKeys(dc);
4446 06 Apr 17 nicklas 852       
4446 06 Apr 17 nicklas 853       // If an annotation type is shared to only ONE project
4446 06 Apr 17 nicklas 854       // we count it as a new annotation type. This allows
4446 06 Apr 17 nicklas 855       // us to set the annotation also on existing items since
4446 06 Apr 17 nicklas 856       // the annotation will not be visible in earlier releases
6784 02 Aug 22 nicklas 857       for (ImportedAnnotationType at : annotationTypes.values())
4446 06 Apr 17 nicklas 858       {
6784 02 Aug 22 nicklas 859         at.projects = at.annotationType.getProjectKey().getProjectIds(Permission.READ);
6784 02 Aug 22 nicklas 860         at.isNewAnnotationType = at.projects.size() == 1;
4446 06 Apr 17 nicklas 861       }
4381 07 Mar 17 nicklas 862     }
4381 07 Mar 17 nicklas 863   }
4368 28 Feb 17 nicklas 864   
4446 06 Apr 17 nicklas 865   @SuppressWarnings({ "rawtypes", "unchecked" })
5189 12 Dec 18 nicklas 866   private void importCohortItems(JSONArray jsonItems, String currentFile, Project updateFrom)
4368 28 Feb 17 nicklas 867   {
4368 28 Feb 17 nicklas 868     
4368 28 Feb 17 nicklas 869     DbControl dc = sc.newDbControl();
4368 28 Feb 17 nicklas 870     try
4368 28 Feb 17 nicklas 871     {
4445 06 Apr 17 nicklas 872       // Stats for this transaction
4445 06 Apr 17 nicklas 873       int numItems = 0;
5189 12 Dec 18 nicklas 874       int numNotFound = 0;
4445 06 Apr 17 nicklas 875       int numItemsCreated = 0;
4445 06 Apr 17 nicklas 876       int numAnnotations = 0;
6784 02 Aug 22 nicklas 877       int numDefaultConverted = 0;
4370 28 Feb 17 nicklas 878       
4372 02 Mar 17 nicklas 879       Set<SharedItem> itemsToShare = new HashSet<>();
4386 09 Mar 17 nicklas 880       namedItems.clear();
4370 28 Feb 17 nicklas 881       
4368 28 Feb 17 nicklas 882       for (int itemNo = 0; itemNo < jsonItems.size(); itemNo++)
4368 28 Feb 17 nicklas 883       {
4441 03 Apr 17 nicklas 884         numItems++;
4441 03 Apr 17 nicklas 885         
4368 28 Feb 17 nicklas 886         JSONObject json = (JSONObject)jsonItems.get(itemNo);
4368 28 Feb 17 nicklas 887         Item itemType = Item.valueOf((String)json.get("type"));
4368 28 Feb 17 nicklas 888         
4372 02 Mar 17 nicklas 889         ItemImporter<?> importer = importers.get(itemType);
4386 09 Mar 17 nicklas 890         if (importer == null)
4368 28 Feb 17 nicklas 891         {
4386 09 Mar 17 nicklas 892           throw new InvalidDataException("Could not find an importer for item type: " + itemType.name());
4386 09 Mar 17 nicklas 893         }
4386 09 Mar 17 nicklas 894         
4386 09 Mar 17 nicklas 895         String name = (String)json.get("name");
4386 09 Mar 17 nicklas 896         BasicItem item = importer.findOrCreateItem(dc, name, json);
5189 12 Dec 18 nicklas 897         if (item == null)
4386 09 Mar 17 nicklas 898         {
5189 12 Dec 18 nicklas 899           numNotFound++;
5189 12 Dec 18 nicklas 900           String subtypeName = (String)json.get("subtype");
5189 12 Dec 18 nicklas 901           String msg = "  Not found " + itemType.name();
5189 12 Dec 18 nicklas 902           if (subtypeName != null) msg += " (" + subtypeName + ")";
5189 12 Dec 18 nicklas 903           if (updateFrom != null) msg += " in project " + updateFrom.getName();
5189 12 Dec 18 nicklas 904           msg += ": " + name;
5189 12 Dec 18 nicklas 905           log(msg);
5189 12 Dec 18 nicklas 906         }
5189 12 Dec 18 nicklas 907         else
5189 12 Dec 18 nicklas 908         {
4386 09 Mar 17 nicklas 909           namedItems.put(name, item);
4386 09 Mar 17 nicklas 910           boolean isNewItem = !item.isInDatabase();
4386 09 Mar 17 nicklas 911
4486 09 May 17 nicklas 912           log((isNewItem ? "  Created " : "  Existing ") + itemType.name() + ": " + name);
4486 09 May 17 nicklas 913           
4386 09 Mar 17 nicklas 914           // Make sure items are shared to the current project
4386 09 Mar 17 nicklas 915           if (item instanceof SharedItem)
4368 28 Feb 17 nicklas 916           {
4386 09 Mar 17 nicklas 917             SharedItem sItem = (SharedItem)item;
4386 09 Mar 17 nicklas 918             itemsToShare.add(sItem);
4386 09 Mar 17 nicklas 919           }
4386 09 Mar 17 nicklas 920
4386 09 Mar 17 nicklas 921           if (isNewItem)
4386 09 Mar 17 nicklas 922           {
4441 03 Apr 17 nicklas 923             numItemsCreated++;
4386 09 Mar 17 nicklas 924             String subtypeName = (String)json.get("subtype");
4386 09 Mar 17 nicklas 925             if (subtypeName != null && item instanceof Subtypable)
4370 28 Feb 17 nicklas 926             {
4411 21 Mar 17 nicklas 927               ItemSubtype subtype = getItemSubtype(subtypes, subtypeName);
4386 09 Mar 17 nicklas 928               Subtypable sItem = (Subtypable)item;
4386 09 Mar 17 nicklas 929               sItem.setItemSubtype(subtype);
4370 28 Feb 17 nicklas 930             }
4386 09 Mar 17 nicklas 931           }
4386 09 Mar 17 nicklas 932           
4405 20 Mar 17 nicklas 933           String dataFilesFolderValue = null;
4386 09 Mar 17 nicklas 934           if (item instanceof Annotatable)
4386 09 Mar 17 nicklas 935           {
5179 05 Dec 18 nicklas 936             Annotatable aItem = (Annotatable)item;
5179 05 Dec 18 nicklas 937             AnnotationSet aSet = null;
5179 05 Dec 18 nicklas 938             if (isNewItem)
5179 05 Dec 18 nicklas 939             {
5179 05 Dec 18 nicklas 940               // Set the FirstReleasedIn annotation
5179 05 Dec 18 nicklas 941               aSet = aItem.getAnnotationSet();
5179 05 Dec 18 nicklas 942               aSet.getAnnotation(firstReleasedIn).setValueIfDifferent(releaseVersion, null);
5179 05 Dec 18 nicklas 943             }
5179 05 Dec 18 nicklas 944             
4386 09 Mar 17 nicklas 945             JSONArray jsonAnnotations = (JSONArray)json.get("annotations");
4386 09 Mar 17 nicklas 946             if (jsonAnnotations != null && jsonAnnotations.size() > 0)
4384 08 Mar 17 nicklas 947             {
5179 05 Dec 18 nicklas 948               if (aSet == null) aSet = aItem.getAnnotationSet();
4386 09 Mar 17 nicklas 949               
4386 09 Mar 17 nicklas 950               for (int aNo = 0; aNo < jsonAnnotations.size(); aNo++)
4384 08 Mar 17 nicklas 951               {
4386 09 Mar 17 nicklas 952                 JSONObject jsonAnnotation = (JSONObject)jsonAnnotations.get(aNo);
4386 09 Mar 17 nicklas 953                 
4386 09 Mar 17 nicklas 954                 String aName = (String)jsonAnnotation.get("name");
6784 02 Aug 22 nicklas 955                 ImportedAnnotationType at = annotationTypes.get(aName);
4386 09 Mar 17 nicklas 956                 if (at == null)
4384 08 Mar 17 nicklas 957                 {
4386 09 Mar 17 nicklas 958                   throw new ItemNotFoundException("AnnotationType["+aName+"]");
4384 08 Mar 17 nicklas 959                 }
6784 02 Aug 22 nicklas 960                 //boolean isNewAnnotationType = at.isNew;
4386 09 Mar 17 nicklas 961                 Object aValue = jsonAnnotation.get("value");
6784 02 Aug 22 nicklas 962                 if (aValue != null)
4467 25 Apr 17 nicklas 963                 {
6784 02 Aug 22 nicklas 964                   Map<String, ?> indexLookup = lookups.get(aName);
6784 02 Aug 22 nicklas 965                   if (indexLookup != null)
6784 02 Aug 22 nicklas 966                   {
6784 02 Aug 22 nicklas 967                     aValue = indexLookup.get((String)aValue);
6784 02 Aug 22 nicklas 968                   }
6784 02 Aug 22 nicklas 969                   Type valueType = at.annotationType.getValueType();
6784 02 Aug 22 nicklas 970                   if (valueType == Type.DATE)
6784 02 Aug 22 nicklas 971                   {
6784 02 Aug 22 nicklas 972                     aValue = dateFormat.convert((String)aValue);
6784 02 Aug 22 nicklas 973                   }
6784 02 Aug 22 nicklas 974                   else if (valueType == Type.TIMESTAMP)
6784 02 Aug 22 nicklas 975                   {
6784 02 Aug 22 nicklas 976                     aValue = dateTimeFormat.convert((String)aValue);
6784 02 Aug 22 nicklas 977                   }
6784 02 Aug 22 nicklas 978                   else if (valueType.isNumerical())
6784 02 Aug 22 nicklas 979                   {
6784 02 Aug 22 nicklas 980                     aValue = valueType.convertNumber((Number)aValue);
6784 02 Aug 22 nicklas 981                   }
4467 25 Apr 17 nicklas 982                 }
4386 09 Mar 17 nicklas 983                 
4386 09 Mar 17 nicklas 984                 Annotation a = null;
6784 02 Aug 22 nicklas 985                 boolean projectAnnotations = at.annotationType.getProjectAnnotations();
4445 06 Apr 17 nicklas 986                 boolean annotationUpdated = false;
4403 17 Mar 17 nicklas 987                 if (at == dataFilesFolder)
4386 09 Mar 17 nicklas 988                 {
4405 20 Mar 17 nicklas 989                   dataFilesFolderValue = (String)aValue;
4403 17 Mar 17 nicklas 990                   // The DataFilesFolder annotation need special handling
5125 21 Nov 18 nicklas 991                   // The default value should always be without a release version prefix
5125 21 Nov 18 nicklas 992                   // All items also get a project-specific value with a release version prefix
6784 02 Aug 22 nicklas 993                   aSet.getProjectAnnotation(at.annotationType, null).setValueIfDifferent(aValue, null);
6784 02 Aug 22 nicklas 994                   a = aSet.getProjectAnnotation(at.annotationType, currentProject);
4441 03 Apr 17 nicklas 995                   aValue = "/" + releaseVersion + aValue;
4386 09 Mar 17 nicklas 996                 }
4403 17 Mar 17 nicklas 997                 else
4386 09 Mar 17 nicklas 998                 {
6784 02 Aug 22 nicklas 999                   if (aValue == null)
4403 17 Mar 17 nicklas 1000                   {
6784 02 Aug 22 nicklas 1001                     // If there is no value for a project-specific annotation we
6784 02 Aug 22 nicklas 1002                     // may have to convert an existing default annotation to
6784 02 Aug 22 nicklas 1003                     // project-specific annotations for all releases the current
6784 02 Aug 22 nicklas 1004                     // item is part of that doesn't have a project-specific value already
6784 02 Aug 22 nicklas 1005                     if (projectAnnotations && !isNewItem & !at.isNewAnnotationType && aSet.hasProjectAnnotation(at.annotationType, Source.PRIMARY, null))
6784 02 Aug 22 nicklas 1006                     {
7021 27 Jan 23 nicklas 1007                       log("    Converting default annotation: "+aName+" (value is null in current project)");
6784 02 Aug 22 nicklas 1008                       // Remove the existing default annotation
6784 02 Aug 22 nicklas 1009                       Annotation aDefault = aSet.getProjectAnnotation(at.annotationType, null);
6784 02 Aug 22 nicklas 1010                       List<?> defaultValues = aDefault.getValues();
6784 02 Aug 22 nicklas 1011                       aSet.removeProjectAnnotation(at.annotationType, null);
6784 02 Aug 22 nicklas 1012                       log("    Annotation removed: " + aName + " (default value)");
6784 02 Aug 22 nicklas 1013                       numDefaultConverted++;
6784 02 Aug 22 nicklas 1014                       
6784 02 Aug 22 nicklas 1015                       // Copy the default values to other projects where the item and annotation type belong
6784 02 Aug 22 nicklas 1016                       Set<Integer> itemProjects = ((SharedItem)aItem).getProjectKey().getProjectIds(Permission.READ);
6784 02 Aug 22 nicklas 1017                       for (int pid : itemProjects)
6784 02 Aug 22 nicklas 1018                       {
6784 02 Aug 22 nicklas 1019                         Project p = Project.getById(dc, pid);
6784 02 Aug 22 nicklas 1020                         if (at.projects.contains(pid) && pid != currentProject.getId())
6784 02 Aug 22 nicklas 1021                         {
6784 02 Aug 22 nicklas 1022                           Annotation aProject = aSet.getProjectAnnotation(at.annotationType, p);
6784 02 Aug 22 nicklas 1023                           if (!aProject.isInDatabase())
6784 02 Aug 22 nicklas 1024                           {
6784 02 Aug 22 nicklas 1025                             aProject.setValuesIfDifferent(defaultValues, null);
6784 02 Aug 22 nicklas 1026                             log("    Annotation copied: " + aName + " (project: " + p.getName() + ")");
6784 02 Aug 22 nicklas 1027                           }
6784 02 Aug 22 nicklas 1028                           else
6784 02 Aug 22 nicklas 1029                           {
6784 02 Aug 22 nicklas 1030                             // Do not overwrite existing project-specific values
6784 02 Aug 22 nicklas 1031                             log("    Annotation existed: " + aName + " (project: " + p.getName() + ")");
6784 02 Aug 22 nicklas 1032                           }
6784 02 Aug 22 nicklas 1033                         }
6784 02 Aug 22 nicklas 1034                         else
6784 02 Aug 22 nicklas 1035                         {
6784 02 Aug 22 nicklas 1036                           log("    Annotation not copied: " + aName + " (project: " + p.getName() + ")");
6784 02 Aug 22 nicklas 1037                         }
6784 02 Aug 22 nicklas 1038                       }
6784 02 Aug 22 nicklas 1039                     }
6784 02 Aug 22 nicklas 1040                     else
6784 02 Aug 22 nicklas 1041                     {
6784 02 Aug 22 nicklas 1042                       log("    Skipping null annotation: "+aName);
6784 02 Aug 22 nicklas 1043                     }
6784 02 Aug 22 nicklas 1044                   }
6784 02 Aug 22 nicklas 1045                   else if ((isNewItem || at.isNewAnnotationType) && projectAnnotations)
6784 02 Aug 22 nicklas 1046                   {
4403 17 Mar 17 nicklas 1047                     // Always set default value
6784 02 Aug 22 nicklas 1048                     a = aSet.getProjectAnnotation(at.annotationType, null);
4403 17 Mar 17 nicklas 1049                   }
6784 02 Aug 22 nicklas 1050                   else if (isNewItem || projectAnnotations || at.isNewAnnotationType)
4403 17 Mar 17 nicklas 1051                   {
4446 06 Apr 17 nicklas 1052                     // * For new item we always set all annotations
4446 06 Apr 17 nicklas 1053                     // * For project annotations we create a new value
4446 06 Apr 17 nicklas 1054                     //   if it is different from the default value
4446 06 Apr 17 nicklas 1055                     // * For new annotation types it is also safe to set
4446 06 Apr 17 nicklas 1056                     //   the annotation since the annotation type is not
4446 06 Apr 17 nicklas 1057                     //   shared to older releases and the annotation value 
4446 06 Apr 17 nicklas 1058                     //   will not be visible (unless logged in as an admin)
6784 02 Aug 22 nicklas 1059                     a = aSet.getAnnotation(at.annotationType);
4403 17 Mar 17 nicklas 1060                   }
4446 06 Apr 17 nicklas 1061                   else
4446 06 Apr 17 nicklas 1062                   {
4446 06 Apr 17 nicklas 1063                     // Existing item and annotation type and no project annotations.
5181 05 Dec 18 nicklas 1064                     // If the annotation doesn't exists yet we should create it, otherwise
5181 05 Dec 18 nicklas 1065                     // we log a message if the value has changed
6784 02 Aug 22 nicklas 1066                     a = aSet.getAnnotation(at.annotationType);
5181 05 Dec 18 nicklas 1067                     if (a.isInDatabase())
4486 09 May 17 nicklas 1068                     {
5181 05 Dec 18 nicklas 1069                       if (a.checkIfDifferent(aValue, null))
5181 05 Dec 18 nicklas 1070                       {
5181 05 Dec 18 nicklas 1071                         log("    Annotation has changed but can't be updated (should it be project-specific?): " + aName);
5181 05 Dec 18 nicklas 1072                       }
5181 05 Dec 18 nicklas 1073                       a = null;
4486 09 May 17 nicklas 1074                     }
4446 06 Apr 17 nicklas 1075                   }
4386 09 Mar 17 nicklas 1076                 }
4441 03 Apr 17 nicklas 1077                 if (a != null) 
4441 03 Apr 17 nicklas 1078                 {
4441 03 Apr 17 nicklas 1079                   annotationUpdated = a.setValueIfDifferent(aValue, null);
4486 09 May 17 nicklas 1080                   if (annotationUpdated) 
4486 09 May 17 nicklas 1081                   {
4486 09 May 17 nicklas 1082                     numAnnotations++;
4486 09 May 17 nicklas 1083                     boolean isNew = !a.isInDatabase();
4486 09 May 17 nicklas 1084                     log("    Annotation " + (isNew ? "created" : "updated") + ": " + aName + (a.getProjectId() !=0 ? " (project-specific)" : "" ));
4486 09 May 17 nicklas 1085                   }
4441 03 Apr 17 nicklas 1086                 }
4384 08 Mar 17 nicklas 1087               }
4384 08 Mar 17 nicklas 1088             }
4368 28 Feb 17 nicklas 1089           }
4405 20 Mar 17 nicklas 1090           
4405 20 Mar 17 nicklas 1091           JSONArray jsonFiles = (JSONArray)json.get("files");
4405 20 Mar 17 nicklas 1092           if (jsonFiles != null && jsonFiles.size() > 0)
4405 20 Mar 17 nicklas 1093           {
4405 20 Mar 17 nicklas 1094             if (dataFilesFolderValue == null)
4405 20 Mar 17 nicklas 1095             {
4405 20 Mar 17 nicklas 1096               throw new InvalidDataException("Found files[] but not DataFilesFolder annotation for item: " + name);
4405 20 Mar 17 nicklas 1097             }
4405 20 Mar 17 nicklas 1098             
4405 20 Mar 17 nicklas 1099             Directory releaseDir = Directory.getByPath(dc, new Path(Relax.RELEASE_DIR, Path.Type.DIRECTORY));
4411 21 Mar 17 nicklas 1100             FileStoreEnabled fsItem = item instanceof FileStoreEnabled ? (FileStoreEnabled)item : null;
4405 20 Mar 17 nicklas 1101             
4405 20 Mar 17 nicklas 1102             for (int fileNo = 0; fileNo < jsonFiles.size(); fileNo++)
4405 20 Mar 17 nicklas 1103             {
4405 20 Mar 17 nicklas 1104               JSONObject jsonFile = (JSONObject)jsonFiles.get(fileNo);
4405 20 Mar 17 nicklas 1105               String fileName = (String)jsonFile.get("name");
4413 21 Mar 17 nicklas 1106               // Files can be linked to an item via either AnyToAny link or DataFileType
4411 21 Mar 17 nicklas 1107               String fileType = (String)jsonFile.get("type");
4413 21 Mar 17 nicklas 1108               String linkName = (String)jsonFile.get("link");
4411 21 Mar 17 nicklas 1109
4411 21 Mar 17 nicklas 1110               // Create/find the find the file in the BASE file system
4405 20 Mar 17 nicklas 1111               String baseFSDir = Relax.RELEASE_DIR + "/" + name.substring(0, 4) + dataFilesFolderValue + "/" + fileName;
4411 21 Mar 17 nicklas 1112               Path baseFSPath = new Path(baseFSDir, Path.Type.FILE);
5194 14 Dec 18 nicklas 1113               
5194 14 Dec 18 nicklas 1114               Directory dir = null;
5194 14 Dec 18 nicklas 1115               File file = null;
5194 14 Dec 18 nicklas 1116               if (updateFrom != null)
4411 21 Mar 17 nicklas 1117               {
5194 14 Dec 18 nicklas 1118                 // It is not allowed to create new items but we get an exception if the file doesn't exists
5194 14 Dec 18 nicklas 1119                 // If the file exists, we must also check that the file is shared to the "updateFrom" project
5194 14 Dec 18 nicklas 1120                 try
5194 14 Dec 18 nicklas 1121                 {
5194 14 Dec 18 nicklas 1122                   file = File.getByPath(dc, baseFSPath, false);
5194 14 Dec 18 nicklas 1123                   dir = file.getDirectory();
5194 14 Dec 18 nicklas 1124                   ProjectKey key = file.getProjectKey();
5194 14 Dec 18 nicklas 1125                   if (key == null || !key.getPermissions(updateFrom).contains(Permission.READ))
5194 14 Dec 18 nicklas 1126                   {
5194 14 Dec 18 nicklas 1127                     file = null;
5194 14 Dec 18 nicklas 1128                     dir = null;
5194 14 Dec 18 nicklas 1129                   }
5194 14 Dec 18 nicklas 1130                 }
5194 14 Dec 18 nicklas 1131                 catch (ItemNotFoundException ex)
5194 14 Dec 18 nicklas 1132                 {}
4411 21 Mar 17 nicklas 1133               }
4411 21 Mar 17 nicklas 1134               else
4411 21 Mar 17 nicklas 1135               {
5194 14 Dec 18 nicklas 1136                 // Allow file and directory creation
5194 14 Dec 18 nicklas 1137                 dir = Directory.getNew(dc, baseFSPath);
5194 14 Dec 18 nicklas 1138                 file = File.getFile(dc, dir, fileName, true);
4411 21 Mar 17 nicklas 1139               }
5194 14 Dec 18 nicklas 1140               if (file == null)
4405 20 Mar 17 nicklas 1141               {
5194 14 Dec 18 nicklas 1142                 String msg = "    Not found";
5194 14 Dec 18 nicklas 1143                 if (fileType != null) msg += " "+fileType;
5194 14 Dec 18 nicklas 1144                 else if (linkName != null) msg += " "+linkName;
5194 14 Dec 18 nicklas 1145                 if (updateFrom != null) msg += " in project " + updateFrom.getName();
5194 14 Dec 18 nicklas 1146                 msg += ": " + fileName;
5194 14 Dec 18 nicklas 1147                 log(msg);
4405 20 Mar 17 nicklas 1148               }
5194 14 Dec 18 nicklas 1149               else
4405 20 Mar 17 nicklas 1150               {
5194 14 Dec 18 nicklas 1151                 boolean isNewFile = !file.isInDatabase();
5194 14 Dec 18 nicklas 1152   
5194 14 Dec 18 nicklas 1153                 // Create URL to the file... if it already exists in the database we
5194 14 Dec 18 nicklas 1154                 // only change the file server, but not the path (which include the first releaseVersion)
5194 14 Dec 18 nicklas 1155                 URI fileURI = null;
5194 14 Dec 18 nicklas 1156                 if (isNewFile)
4411 21 Mar 17 nicklas 1157                 {
5194 14 Dec 18 nicklas 1158                   fileURI = URI.create("sftp://"+fileServer.getHost()+"/"+releaseVersion+dataFilesFolderValue+"/"+fileName);
5194 14 Dec 18 nicklas 1159                 }
5194 14 Dec 18 nicklas 1160                 else
5194 14 Dec 18 nicklas 1161                 {
5194 14 Dec 18 nicklas 1162                   fileURI = file.getURI();
5194 14 Dec 18 nicklas 1163                   fileURI = URI.create("sftp://"+fileServer.getHost()+fileURI.getPath());
5194 14 Dec 18 nicklas 1164                 }
5194 14 Dec 18 nicklas 1165                 
5195 14 Dec 18 nicklas 1166                 // Create metadata from the JSON data -- use existing values if not present in jsonFile
5194 14 Dec 18 nicklas 1167                 UriMetadata metadata = new UriMetadata(fileURI);
5195 14 Dec 18 nicklas 1168                 metadata.setCharacterSet(getFirstNotNull((String)jsonFile.get("characterSet"), file.getCharacterSet()));
5195 14 Dec 18 nicklas 1169                 metadata.setMimeType(getFirstNotNull((String)jsonFile.get("mimeType"), file.getMimeType()));
5195 14 Dec 18 nicklas 1170                 metadata.setLastModified(getFirstNotNull(timeStampFormat.convert((String)jsonFile.get("lastUpdate")), file.getLastUpdate()));
5195 14 Dec 18 nicklas 1171                 metadata.setLength(getFirstNotNull((Long)jsonFile.get("size"), file.getSize()));
5194 14 Dec 18 nicklas 1172
5194 14 Dec 18 nicklas 1173                 // Set file server, URL and other metadata
5194 14 Dec 18 nicklas 1174                 file.setFileServer(fileServer);
5194 14 Dec 18 nicklas 1175                 file.setUrl(fileURI.toString(), metadata);
5194 14 Dec 18 nicklas 1176                 
5194 14 Dec 18 nicklas 1177                 // Ensure permissions are correct for the file and all parent directories
5194 14 Dec 18 nicklas 1178                 itemsToShare.add(file);
5194 14 Dec 18 nicklas 1179                 if (isNewFile) 
5194 14 Dec 18 nicklas 1180                 {
5194 14 Dec 18 nicklas 1181                   dc.saveItem(file);
5194 14 Dec 18 nicklas 1182                   file.setProjectKey(null);
5194 14 Dec 18 nicklas 1183                 }
5194 14 Dec 18 nicklas 1184                 while (!dir.equals(releaseDir))
5194 14 Dec 18 nicklas 1185                 {
5194 14 Dec 18 nicklas 1186                   itemsToShare.add(dir);
5194 14 Dec 18 nicklas 1187                   if (!dir.isInDatabase()) dir.setProjectKey(null);
5194 14 Dec 18 nicklas 1188                   dir = dir.getParent();
5194 14 Dec 18 nicklas 1189                 }
5194 14 Dec 18 nicklas 1190                 
5194 14 Dec 18 nicklas 1191                 // If "type" was set, we link the file to the item
5194 14 Dec 18 nicklas 1192                 if (isNewFile || isNewItem)
5194 14 Dec 18 nicklas 1193                 {
5194 14 Dec 18 nicklas 1194                   if (fileType != null && fsItem != null)
4413 21 Mar 17 nicklas 1195                   {
5194 14 Dec 18 nicklas 1196                     DataFileType dft = fileTypes.get(fileType);
5194 14 Dec 18 nicklas 1197                     if (dft == null)
5194 14 Dec 18 nicklas 1198                     {
5194 14 Dec 18 nicklas 1199                       throw new ItemNotFoundException("DataFileType["+fileType+"]");
5194 14 Dec 18 nicklas 1200                     }
5194 14 Dec 18 nicklas 1201                     fsItem.getFileSet().addMember(file, dft);
5194 14 Dec 18 nicklas 1202                     log("    Linked " + fileType + ": " + fileName);
4413 21 Mar 17 nicklas 1203                   }
5194 14 Dec 18 nicklas 1204                   else if (linkName != null)
5194 14 Dec 18 nicklas 1205                   {
5194 14 Dec 18 nicklas 1206                     AnyToAny link = AnyToAny.getNewOrExisting(dc, item, linkName, file, false);
5194 14 Dec 18 nicklas 1207                     if (!link.isInDatabase()) dc.saveItem(link);
5194 14 Dec 18 nicklas 1208                     log("    Linked " + linkName + ": " + fileName);
5194 14 Dec 18 nicklas 1209                   }
4411 21 Mar 17 nicklas 1210                 }
5194 14 Dec 18 nicklas 1211                 else
4413 21 Mar 17 nicklas 1212                 {
5194 14 Dec 18 nicklas 1213                   // TODO -- do we need to care about this
5194 14 Dec 18 nicklas 1214                   // Existing file and existing item...
4486 09 May 17 nicklas 1215                 }
4411 21 Mar 17 nicklas 1216               }
4405 20 Mar 17 nicklas 1217             }
4405 20 Mar 17 nicklas 1218           }
4368 28 Feb 17 nicklas 1219         }
4368 28 Feb 17 nicklas 1220       }
4368 28 Feb 17 nicklas 1221       
4372 02 Mar 17 nicklas 1222       // Link with parent(s)
4370 28 Feb 17 nicklas 1223       for (int itemNo = 0; itemNo < jsonItems.size(); itemNo++)
4370 28 Feb 17 nicklas 1224       {
4370 28 Feb 17 nicklas 1225         JSONObject json = (JSONObject)jsonItems.get(itemNo);
4370 28 Feb 17 nicklas 1226         String name = (String)json.get("name");
4370 28 Feb 17 nicklas 1227         
4386 09 Mar 17 nicklas 1228         BasicItem item = namedItems.get(name);
5189 12 Dec 18 nicklas 1229         if (item != null && !item.isInDatabase())
4370 28 Feb 17 nicklas 1230         {
4386 09 Mar 17 nicklas 1231           ItemImporter importer = importers.get(item.getType());
4386 09 Mar 17 nicklas 1232           importer.linkToParents(dc, item, json);
4370 28 Feb 17 nicklas 1233         }
4370 28 Feb 17 nicklas 1234       }
4370 28 Feb 17 nicklas 1235       
4386 09 Mar 17 nicklas 1236       // Update the permissions to READ for the current project
5189 12 Dec 18 nicklas 1237       if (sc.getActiveProjectId() != 0 && itemsToShare.size() > 0)
4368 28 Feb 17 nicklas 1238       {
4368 28 Feb 17 nicklas 1239         MultiPermissions mp = new MultiPermissions(itemsToShare);
4368 28 Feb 17 nicklas 1240         mp.addPermissions(Project.getById(dc, sc.getActiveProjectId()), EnumSet.of(Permission.READ));
4368 28 Feb 17 nicklas 1241         mp.updateKeys(dc);
4368 28 Feb 17 nicklas 1242       }
4368 28 Feb 17 nicklas 1243       
4368 28 Feb 17 nicklas 1244       dc.commit();
4445 06 Apr 17 nicklas 1245       
4445 06 Apr 17 nicklas 1246       // Update statistics AFTER commit, if we do it before we may count items twice
4445 06 Apr 17 nicklas 1247       // if the job is PAUSED and RESUMED
4445 06 Apr 17 nicklas 1248       state.numItems += numItems;
5189 12 Dec 18 nicklas 1249       state.numNotFound += numNotFound;
4445 06 Apr 17 nicklas 1250       state.numItemsCreated += numItemsCreated;
4445 06 Apr 17 nicklas 1251       state.numAnnotations += numAnnotations;
6784 02 Aug 22 nicklas 1252       state.numDefaultConverted += numDefaultConverted;
4486 09 May 17 nicklas 1253       
6784 02 Aug 22 nicklas 1254       log("  Completed file '" + currentFile + "': " + numItems + " items; " + numItemsCreated + " new; " + numNotFound + " not found; "+
6784 02 Aug 22 nicklas 1255         numAnnotations + " annotations created or updated; "+ numDefaultConverted + " default values converted");
4368 28 Feb 17 nicklas 1256     }
4368 28 Feb 17 nicklas 1257     finally
4368 28 Feb 17 nicklas 1258     {
4368 28 Feb 17 nicklas 1259       if (dc != null) dc.close();
4368 28 Feb 17 nicklas 1260     }
4368 28 Feb 17 nicklas 1261   }
4368 28 Feb 17 nicklas 1262   
4411 21 Mar 17 nicklas 1263   /**
4411 21 Mar 17 nicklas 1264     Find a named item subtype. If subtypeName is null, null is returned. 
4411 21 Mar 17 nicklas 1265     If no subtype with the given name is found an exception is thrown.
4411 21 Mar 17 nicklas 1266   */
4411 21 Mar 17 nicklas 1267   static ItemSubtype getItemSubtype(Map<String, ItemSubtype> subtypes, String subtypeName)
4411 21 Mar 17 nicklas 1268   {
4411 21 Mar 17 nicklas 1269     ItemSubtype subtype = null;
4411 21 Mar 17 nicklas 1270     if (subtypeName != null)
4411 21 Mar 17 nicklas 1271     {
4411 21 Mar 17 nicklas 1272       subtype = subtypes.get(subtypeName);
4411 21 Mar 17 nicklas 1273       if (subtype == null)
4411 21 Mar 17 nicklas 1274       {
4411 21 Mar 17 nicklas 1275         throw new ItemNotFoundException("Subtype["+subtypeName+"]");
4411 21 Mar 17 nicklas 1276       }
4411 21 Mar 17 nicklas 1277     }
4411 21 Mar 17 nicklas 1278     return subtype;
4411 21 Mar 17 nicklas 1279   }
4411 21 Mar 17 nicklas 1280   
5195 14 Dec 18 nicklas 1281   /**
5195 14 Dec 18 nicklas 1282     Get the first non-null value in the array if there is one.
5195 14 Dec 18 nicklas 1283   */
5195 14 Dec 18 nicklas 1284   @SafeVarargs
5195 14 Dec 18 nicklas 1285   static <T> T getFirstNotNull(T... values)
5195 14 Dec 18 nicklas 1286   {
5195 14 Dec 18 nicklas 1287     if (values != null)
5195 14 Dec 18 nicklas 1288     {
5195 14 Dec 18 nicklas 1289       for (T v : values)
5195 14 Dec 18 nicklas 1290       {
5195 14 Dec 18 nicklas 1291         if (v != null) return v;
5195 14 Dec 18 nicklas 1292       }
5195 14 Dec 18 nicklas 1293     }
5195 14 Dec 18 nicklas 1294     return null;
5195 14 Dec 18 nicklas 1295   }
5195 14 Dec 18 nicklas 1296   
4386 09 Mar 17 nicklas 1297   abstract class ItemImporter<I extends BasicItem & Nameable>
4372 02 Mar 17 nicklas 1298   {
4372 02 Mar 17 nicklas 1299     
4372 02 Mar 17 nicklas 1300     private final ItemQuery<I> query;
5189 12 Dec 18 nicklas 1301     private final boolean updateOnly;
4403 17 Mar 17 nicklas 1302     private final Map<String, ? extends BasicItem> namedItems;
4411 21 Mar 17 nicklas 1303     private final boolean shareable;
4411 21 Mar 17 nicklas 1304     private final boolean callUpdateAfterCreate;
4372 02 Mar 17 nicklas 1305     
4386 09 Mar 17 nicklas 1306     /**
4386 09 Mar 17 nicklas 1307       Creates a new item importer. The given
4386 09 Mar 17 nicklas 1308       query will be configured to match ALL items
4386 09 Mar 17 nicklas 1309       with a filter on the "name" property.
4386 09 Mar 17 nicklas 1310       The 'namedItems' is a cache of items (mixed types) that is 
4386 09 Mar 17 nicklas 1311       handled in the current transaction (=JSON file). It is populated 
4386 09 Mar 17 nicklas 1312       outside of the importers but is needed for looking up and linking with
4386 09 Mar 17 nicklas 1313       parent items.
4386 09 Mar 17 nicklas 1314     */
5189 12 Dec 18 nicklas 1315     ItemImporter(ItemQuery<I> query, Project updateFrom, Map<String, ? extends BasicItem> namedItems, boolean callUpdateAfterCreate)
4372 02 Mar 17 nicklas 1316     {
4372 02 Mar 17 nicklas 1317       this.query = query;
4372 02 Mar 17 nicklas 1318       query.setIncludes(Include.ALL);
4386 09 Mar 17 nicklas 1319       query.restrict(
4386 09 Mar 17 nicklas 1320         Restrictions.eq(
4386 09 Mar 17 nicklas 1321           Hql.property("name"), 
4386 09 Mar 17 nicklas 1322           Expressions.parameter("name", Type.STRING)
4386 09 Mar 17 nicklas 1323       ));
5189 12 Dec 18 nicklas 1324       
5189 12 Dec 18 nicklas 1325       // If a project is specified we should only look for items that are shared
5189 12 Dec 18 nicklas 1326       // to that project
5189 12 Dec 18 nicklas 1327       this.updateOnly = updateFrom != null;
5189 12 Dec 18 nicklas 1328       if (updateOnly)
5189 12 Dec 18 nicklas 1329       {
5189 12 Dec 18 nicklas 1330         query.restrict(Hql.sharedTo(true, null, null, 
5189 12 Dec 18 nicklas 1331           Restrictions.eq(Hql.property("prj", "id"), Expressions.integer(updateFrom.getId()))));
5189 12 Dec 18 nicklas 1332       }
5189 12 Dec 18 nicklas 1333       
4411 21 Mar 17 nicklas 1334       this.shareable = Shareable.class.isAssignableFrom(query.getItemType().getItemClass());
4411 21 Mar 17 nicklas 1335       this.callUpdateAfterCreate = callUpdateAfterCreate;
4386 09 Mar 17 nicklas 1336       this.namedItems = namedItems;
4372 02 Mar 17 nicklas 1337     }
4372 02 Mar 17 nicklas 1338     
4386 09 Mar 17 nicklas 1339     /**
4386 09 Mar 17 nicklas 1340       Find an existing or create a new item with the given name.
4386 09 Mar 17 nicklas 1341       If an existing item is found the {@link #update(DbControl, BasicItem, String, JSONObject)}
4386 09 Mar 17 nicklas 1342       method is called, otherwise the {@link #create(DbControl, String, JSONObject)}.
4386 09 Mar 17 nicklas 1343       The new item is automatically saved.
4386 09 Mar 17 nicklas 1344      */
5189 12 Dec 18 nicklas 1345     final I findOrCreateItem(DbControl dc, String name, JSONObject json)
4372 02 Mar 17 nicklas 1346     {
4386 09 Mar 17 nicklas 1347       I item = find(dc, name, json);
4386 09 Mar 17 nicklas 1348       if (item == null) 
4372 02 Mar 17 nicklas 1349       {
5189 12 Dec 18 nicklas 1350         if (!updateOnly)
5189 12 Dec 18 nicklas 1351         {
5189 12 Dec 18 nicklas 1352           item = create(dc, name, json);
5189 12 Dec 18 nicklas 1353           item.setName(name);
5189 12 Dec 18 nicklas 1354           if (shareable) ((Shareable)item).setProjectKey(null);
5189 12 Dec 18 nicklas 1355           dc.saveItem(item);
5189 12 Dec 18 nicklas 1356           if (callUpdateAfterCreate) update(dc, item, name, json);
5189 12 Dec 18 nicklas 1357         }
4372 02 Mar 17 nicklas 1358       }
4386 09 Mar 17 nicklas 1359       else
4372 02 Mar 17 nicklas 1360       {
4386 09 Mar 17 nicklas 1361         update(dc, item, name, json);
4386 09 Mar 17 nicklas 1362       }
4386 09 Mar 17 nicklas 1363       
4386 09 Mar 17 nicklas 1364       return item;
4386 09 Mar 17 nicklas 1365     }
4386 09 Mar 17 nicklas 1366     
4386 09 Mar 17 nicklas 1367     /**
4386 09 Mar 17 nicklas 1368       Find an item with the given name. Returns the found
4386 09 Mar 17 nicklas 1369       item or null if none is found. Throws an exception
4386 09 Mar 17 nicklas 1370       if more than one item is found.
4386 09 Mar 17 nicklas 1371     */
4446 06 Apr 17 nicklas 1372     @SuppressWarnings("unchecked")
5189 12 Dec 18 nicklas 1373     final I find(DbControl dc, String name, JSONObject json)
4386 09 Mar 17 nicklas 1374     {
4386 09 Mar 17 nicklas 1375       I item = null;
4403 17 Mar 17 nicklas 1376       if (namedItems != null) item = (I)namedItems.get(name);
4403 17 Mar 17 nicklas 1377       if (item == null)
4386 09 Mar 17 nicklas 1378       {
4403 17 Mar 17 nicklas 1379         query.setParameter("name", name, Type.STRING);
4403 17 Mar 17 nicklas 1380         List<I> result = query.list(dc);
4403 17 Mar 17 nicklas 1381         if (result.size() == 1)
4403 17 Mar 17 nicklas 1382         {
4403 17 Mar 17 nicklas 1383           item = result.get(0);
4403 17 Mar 17 nicklas 1384         }
4403 17 Mar 17 nicklas 1385         else if (result.size() > 1)
4403 17 Mar 17 nicklas 1386         {
4403 17 Mar 17 nicklas 1387           throw new InvalidDataException("Found more than one " + query.getItemType().name() + " with name: "+ name);
4403 17 Mar 17 nicklas 1388         }
4372 02 Mar 17 nicklas 1389       }
4372 02 Mar 17 nicklas 1390       return item;
4372 02 Mar 17 nicklas 1391     }
4372 02 Mar 17 nicklas 1392     
4386 09 Mar 17 nicklas 1393     /**
4386 09 Mar 17 nicklas 1394       Create a new item with the given name.
4386 09 Mar 17 nicklas 1395     */
4381 07 Mar 17 nicklas 1396     abstract I create(DbControl dc, String name, JSONObject json);
4372 02 Mar 17 nicklas 1397     
4386 09 Mar 17 nicklas 1398     /**
4386 09 Mar 17 nicklas 1399       Update an existing item.
4386 09 Mar 17 nicklas 1400     */
4386 09 Mar 17 nicklas 1401     void update(DbControl dc, I item, String name, JSONObject json)
4386 09 Mar 17 nicklas 1402     {}
4386 09 Mar 17 nicklas 1403     
4386 09 Mar 17 nicklas 1404     /**
4386 09 Mar 17 nicklas 1405       Link the item to it parent item. This method is only called
4386 09 Mar 17 nicklas 1406       for new items. The namedItems parameter contains a map of
4386 09 Mar 17 nicklas 1407       all other items created in this transaction. Linking to
4386 09 Mar 17 nicklas 1408       other items that already exists in the database are 
4386 09 Mar 17 nicklas 1409       best done in the create or update methods.
4386 09 Mar 17 nicklas 1410     */
4386 09 Mar 17 nicklas 1411     void linkToParents(DbControl dc, I child, JSONObject json)
4386 09 Mar 17 nicklas 1412     {}
4386 09 Mar 17 nicklas 1413
4386 09 Mar 17 nicklas 1414     /**
4386 09 Mar 17 nicklas 1415       Helper method for getting a named parent item. Throws an exception
4386 09 Mar 17 nicklas 1416       if a parentName is given but not found or if the found parent is
4386 09 Mar 17 nicklas 1417       not of the specified class.
4386 09 Mar 17 nicklas 1418       @param child The current child item (used for error messages)
4386 09 Mar 17 nicklas 1419       @param parentName The name of the parent item
4386 09 Mar 17 nicklas 1420       @param clazz Required class of the parent item (null to allow any class)
4386 09 Mar 17 nicklas 1421     */
4386 09 Mar 17 nicklas 1422     @SuppressWarnings("unchecked")
4386 09 Mar 17 nicklas 1423     <T extends BasicItem> T getParentOfClass(I child, String parentName, Class<T> clazz)
4386 09 Mar 17 nicklas 1424     {
4386 09 Mar 17 nicklas 1425       if (parentName == null) return null;
4386 09 Mar 17 nicklas 1426       BasicItem parent = namedItems.get(parentName);
4386 09 Mar 17 nicklas 1427       if (parent == null)
4386 09 Mar 17 nicklas 1428       {
4386 09 Mar 17 nicklas 1429         throw new ItemNotFoundException("Parent item to '" + child.getName() + "' with name: " + parentName);
4386 09 Mar 17 nicklas 1430       }
4386 09 Mar 17 nicklas 1431       if (clazz != null && !clazz.isInstance(parent))
4386 09 Mar 17 nicklas 1432       {
4386 09 Mar 17 nicklas 1433         throw new ClassCastException("Parent item to '" + child.getName() + "' with name '" + parentName + "' "
4386 09 Mar 17 nicklas 1434             + "is not of the expected class: " + clazz.getName());
4386 09 Mar 17 nicklas 1435       }
4386 09 Mar 17 nicklas 1436       return (T)parent;
4386 09 Mar 17 nicklas 1437     }
4386 09 Mar 17 nicklas 1438     
4372 02 Mar 17 nicklas 1439   }
4372 02 Mar 17 nicklas 1440   
4386 09 Mar 17 nicklas 1441   /**
4386 09 Mar 17 nicklas 1442     Biosource importer.
4386 09 Mar 17 nicklas 1443   */
4372 02 Mar 17 nicklas 1444   class BioSourceImporter
4386 09 Mar 17 nicklas 1445     extends ItemImporter<BioSource>
4372 02 Mar 17 nicklas 1446   {
4372 02 Mar 17 nicklas 1447   
5189 12 Dec 18 nicklas 1448     BioSourceImporter(Project updateFrom, Map<String, BasicItem> namedItems)
4372 02 Mar 17 nicklas 1449     {
5189 12 Dec 18 nicklas 1450       super(BioSource.getQuery(), updateFrom, namedItems, false);
4372 02 Mar 17 nicklas 1451     }
4372 02 Mar 17 nicklas 1452     
4372 02 Mar 17 nicklas 1453     @Override
4381 07 Mar 17 nicklas 1454     BioSource create(DbControl dc, String name, JSONObject json)
4372 02 Mar 17 nicklas 1455     {
4395 13 Mar 17 nicklas 1456       BioSource bs = BioSource.getNew(dc);
4395 13 Mar 17 nicklas 1457       bs.setEntryDate(dateFormat.convert((String)json.get("registrationDate")));
4395 13 Mar 17 nicklas 1458       return bs;
4372 02 Mar 17 nicklas 1459     }
4372 02 Mar 17 nicklas 1460   }
4372 02 Mar 17 nicklas 1461
4386 09 Mar 17 nicklas 1462   /**
4386 09 Mar 17 nicklas 1463     Sample importer.
4386 09 Mar 17 nicklas 1464   */
4372 02 Mar 17 nicklas 1465   class SampleImporter
4386 09 Mar 17 nicklas 1466     extends ItemImporter<Sample>
4372 02 Mar 17 nicklas 1467   {
4372 02 Mar 17 nicklas 1468
5189 12 Dec 18 nicklas 1469     SampleImporter(Project updateFrom, Map<String, BasicItem> namedItems)
4372 02 Mar 17 nicklas 1470     {
5189 12 Dec 18 nicklas 1471       super(Sample.getQuery(), updateFrom, namedItems, false);
4372 02 Mar 17 nicklas 1472     }
4372 02 Mar 17 nicklas 1473     
4372 02 Mar 17 nicklas 1474     @Override
4381 07 Mar 17 nicklas 1475     Sample create(DbControl dc, String name, JSONObject json)
4372 02 Mar 17 nicklas 1476     {
4395 13 Mar 17 nicklas 1477       Sample s = Sample.getNew(dc);
4397 13 Mar 17 nicklas 1478       BioMaterialEvent creationEvent = s.getCreationEvent();
4395 13 Mar 17 nicklas 1479       s.setEntryDate(dateFormat.convert((String)json.get("registrationDate")));
4397 13 Mar 17 nicklas 1480       creationEvent.setEventDate(dateFormat.convert((String)json.get("creationDate")));
4395 13 Mar 17 nicklas 1481       return s;
4372 02 Mar 17 nicklas 1482     }
4372 02 Mar 17 nicklas 1483     
4386 09 Mar 17 nicklas 1484     /**
4386 09 Mar 17 nicklas 1485       The parent can be either another sample or a biosource.
4386 09 Mar 17 nicklas 1486       Lucklily, the same code works for both cases.
4386 09 Mar 17 nicklas 1487     */
4372 02 Mar 17 nicklas 1488     @Override
4386 09 Mar 17 nicklas 1489     public void linkToParents(DbControl dc, Sample s, JSONObject json)
4372 02 Mar 17 nicklas 1490     {
4386 09 Mar 17 nicklas 1491       String parentName = (String)json.get("parent");
4386 09 Mar 17 nicklas 1492       BioMaterial parent = getParentOfClass(s, parentName, BioMaterial.class);
4386 09 Mar 17 nicklas 1493       if (parent != null)
4372 02 Mar 17 nicklas 1494       {
4386 09 Mar 17 nicklas 1495         s.getCreationEvent().setSource(parent);
4372 02 Mar 17 nicklas 1496       }
4372 02 Mar 17 nicklas 1497     }
4372 02 Mar 17 nicklas 1498   }
4372 02 Mar 17 nicklas 1499   
4386 09 Mar 17 nicklas 1500   /**
4386 09 Mar 17 nicklas 1501     Extract importer.
4386 09 Mar 17 nicklas 1502   */
4372 02 Mar 17 nicklas 1503   class ExtractImporter
4386 09 Mar 17 nicklas 1504     extends ItemImporter<Extract>
4372 02 Mar 17 nicklas 1505   {
4372 02 Mar 17 nicklas 1506   
5189 12 Dec 18 nicklas 1507     ExtractImporter(Project updateFrom, Map<String, BasicItem> namedItems)
4372 02 Mar 17 nicklas 1508     {
5189 12 Dec 18 nicklas 1509       super(Extract.getQuery(), updateFrom, namedItems, false);
4372 02 Mar 17 nicklas 1510     }
4372 02 Mar 17 nicklas 1511     
4372 02 Mar 17 nicklas 1512     @Override
4381 07 Mar 17 nicklas 1513     Extract create(DbControl dc, String name, JSONObject json)
4372 02 Mar 17 nicklas 1514     {
4395 13 Mar 17 nicklas 1515       Extract e = Extract.getNew(dc);
4397 13 Mar 17 nicklas 1516       BioMaterialEvent creationEvent = e.getCreationEvent();
4395 13 Mar 17 nicklas 1517       e.setEntryDate(dateFormat.convert((String)json.get("registrationDate")));
4397 13 Mar 17 nicklas 1518       creationEvent.setEventDate(dateFormat.convert((String)json.get("creationDate")));
4395 13 Mar 17 nicklas 1519       return e;
4372 02 Mar 17 nicklas 1520     }
4372 02 Mar 17 nicklas 1521     
4386 09 Mar 17 nicklas 1522     /**
4386 09 Mar 17 nicklas 1523       The parent can be either another extract or a sample.
4386 09 Mar 17 nicklas 1524       Lucklily, the same code works for both cases.
4386 09 Mar 17 nicklas 1525     */
4372 02 Mar 17 nicklas 1526     @Override
4386 09 Mar 17 nicklas 1527     public void linkToParents(DbControl dc, Extract e, JSONObject json)
4372 02 Mar 17 nicklas 1528     {
4386 09 Mar 17 nicklas 1529       String parentName = (String)json.get("parent");
4386 09 Mar 17 nicklas 1530       BioMaterial parent = getParentOfClass(e, parentName, BioMaterial.class);
4386 09 Mar 17 nicklas 1531       if (parent != null)
4372 02 Mar 17 nicklas 1532       {
4386 09 Mar 17 nicklas 1533         e.getCreationEvent().setSource(parent);
4372 02 Mar 17 nicklas 1534       }
4372 02 Mar 17 nicklas 1535     }
4372 02 Mar 17 nicklas 1536   }
4372 02 Mar 17 nicklas 1537
4386 09 Mar 17 nicklas 1538   /**
4386 09 Mar 17 nicklas 1539     Derived bioassay importer.
4386 09 Mar 17 nicklas 1540   */
4372 02 Mar 17 nicklas 1541   class DerivedBioAssayImporter
4386 09 Mar 17 nicklas 1542     extends ItemImporter<DerivedBioAssay>
4372 02 Mar 17 nicklas 1543   {
4372 02 Mar 17 nicklas 1544   
5189 12 Dec 18 nicklas 1545     DerivedBioAssayImporter(Project updateFrom, Map<String, BasicItem> namedItems)
4372 02 Mar 17 nicklas 1546     {
5189 12 Dec 18 nicklas 1547       super(DerivedBioAssay.getQuery(), updateFrom, namedItems, false);
4372 02 Mar 17 nicklas 1548     }
4372 02 Mar 17 nicklas 1549     
4372 02 Mar 17 nicklas 1550     @Override
4381 07 Mar 17 nicklas 1551     DerivedBioAssay create(DbControl dc, String name, JSONObject json)
4372 02 Mar 17 nicklas 1552     {
4395 13 Mar 17 nicklas 1553       DerivedBioAssay dba = DerivedBioAssay.getNew(dc, true, null);
4395 13 Mar 17 nicklas 1554       dba.setEntryDate(dateFormat.convert((String)json.get("registrationDate")));
4395 13 Mar 17 nicklas 1555       return dba;
4372 02 Mar 17 nicklas 1556     }
4372 02 Mar 17 nicklas 1557     
4386 09 Mar 17 nicklas 1558     /**
4386 09 Mar 17 nicklas 1559       We have links to both a parent extract and maybe also to another
4386 09 Mar 17 nicklas 1560       derived bioassay.
4386 09 Mar 17 nicklas 1561      */
4372 02 Mar 17 nicklas 1562     @Override
4386 09 Mar 17 nicklas 1563     public void linkToParents(DbControl dc, DerivedBioAssay dba, JSONObject json)
4372 02 Mar 17 nicklas 1564     {
4386 09 Mar 17 nicklas 1565       DerivedBioAssay parent = getParentOfClass(dba, (String)json.get("parent"), DerivedBioAssay.class);
4386 09 Mar 17 nicklas 1566       if (parent != null) dba.addParent(parent);
4386 09 Mar 17 nicklas 1567
4386 09 Mar 17 nicklas 1568       Extract extract = getParentOfClass(dba, (String)json.get("extract"), Extract.class);
4386 09 Mar 17 nicklas 1569       if (extract != null) dba.setExtract(extract);
4372 02 Mar 17 nicklas 1570     }
4372 02 Mar 17 nicklas 1571   }
4372 02 Mar 17 nicklas 1572
4386 09 Mar 17 nicklas 1573   /**
4386 09 Mar 17 nicklas 1574     Raw bioassay importer.
4386 09 Mar 17 nicklas 1575   */
4372 02 Mar 17 nicklas 1576   class RawBioAssayImporter
4386 09 Mar 17 nicklas 1577     extends ItemImporter<RawBioAssay>
4372 02 Mar 17 nicklas 1578   {
4372 02 Mar 17 nicklas 1579   
5189 12 Dec 18 nicklas 1580     RawBioAssayImporter(Project updateFrom, Map<String, BasicItem> namedItems)
4372 02 Mar 17 nicklas 1581     {
5189 12 Dec 18 nicklas 1582       super(RawBioAssay.getQuery(), updateFrom, namedItems, false);
4372 02 Mar 17 nicklas 1583     }
4372 02 Mar 17 nicklas 1584     
4372 02 Mar 17 nicklas 1585     @Override
4381 07 Mar 17 nicklas 1586     RawBioAssay create(DbControl dc, String name, JSONObject json)
4372 02 Mar 17 nicklas 1587     {
4436 30 Mar 17 nicklas 1588       RawDataType rawDataType = RawDataTypes.getRawDataType((String)json.get("rawdatatype"));
4436 30 Mar 17 nicklas 1589       String variantId = (String)json.get("platformVariant");
4436 30 Mar 17 nicklas 1590       String platformId = (String)json.get("platform");
4436 30 Mar 17 nicklas 1591       RawBioAssay rba = null;
4436 30 Mar 17 nicklas 1592       if (variantId != null)
4436 30 Mar 17 nicklas 1593       {
4436 30 Mar 17 nicklas 1594         PlatformVariant variant = PlatformVariant.getByExternalId(dc, variantId);
4436 30 Mar 17 nicklas 1595         rba = RawBioAssay.getNew(dc, variant, rawDataType);
4436 30 Mar 17 nicklas 1596       }
4436 30 Mar 17 nicklas 1597       else if (platformId != null)
4436 30 Mar 17 nicklas 1598       {
4436 30 Mar 17 nicklas 1599         Platform platform = Platform.getByExternalId(dc, platformId);
4436 30 Mar 17 nicklas 1600         rba = RawBioAssay.getNew(dc, platform, rawDataType);
4436 30 Mar 17 nicklas 1601       }
4436 30 Mar 17 nicklas 1602       else
4436 30 Mar 17 nicklas 1603       {
4436 30 Mar 17 nicklas 1604         throw new InvalidDataException("No platform or variant specified for raw bioassay: "+name);
4436 30 Mar 17 nicklas 1605       }
4395 13 Mar 17 nicklas 1606       rba.setEntryDate(dateFormat.convert((String)json.get("registrationDate")));
4395 13 Mar 17 nicklas 1607       return rba;
4372 02 Mar 17 nicklas 1608     }
4386 09 Mar 17 nicklas 1609
4386 09 Mar 17 nicklas 1610     /**
4386 09 Mar 17 nicklas 1611       We have links to both a parent extract and maybe also to another
4386 09 Mar 17 nicklas 1612       derived bioassay.
4386 09 Mar 17 nicklas 1613      */
4372 02 Mar 17 nicklas 1614     @Override
4386 09 Mar 17 nicklas 1615     public void linkToParents(DbControl dc, RawBioAssay rba, JSONObject json)
4372 02 Mar 17 nicklas 1616     {
4386 09 Mar 17 nicklas 1617       DerivedBioAssay parent = getParentOfClass(rba, (String)json.get("parent"), DerivedBioAssay.class);
4386 09 Mar 17 nicklas 1618       if (parent != null) rba.setParentBioAssay(parent);
4386 09 Mar 17 nicklas 1619   
4386 09 Mar 17 nicklas 1620       Extract extract = getParentOfClass(rba, (String)json.get("extract"), Extract.class);
4386 09 Mar 17 nicklas 1621       if (extract != null) rba.setParentExtract(extract);
4372 02 Mar 17 nicklas 1622     }
4386 09 Mar 17 nicklas 1623
4372 02 Mar 17 nicklas 1624   }
4372 02 Mar 17 nicklas 1625   
4386 09 Mar 17 nicklas 1626   /**
4386 09 Mar 17 nicklas 1627     Annotation type importer.
4386 09 Mar 17 nicklas 1628   */
4381 07 Mar 17 nicklas 1629   class AnnotationTypeImporter
4386 09 Mar 17 nicklas 1630     extends ItemImporter<AnnotationType>
4381 07 Mar 17 nicklas 1631   {
4411 21 Mar 17 nicklas 1632     
4411 21 Mar 17 nicklas 1633     private final Map<String, AnnotationTypeCategory> categories;
4411 21 Mar 17 nicklas 1634     private final List<SharedItem> itemsToShare;
4411 21 Mar 17 nicklas 1635     
6784 02 Aug 22 nicklas 1636     AnnotationTypeImporter(Map<String, AnnotationTypeCategory> categories, List<SharedItem> itemsToShare)
4381 07 Mar 17 nicklas 1637     {
6784 02 Aug 22 nicklas 1638       super(AnnotationType.getQuery(null), null, null, true);
4411 21 Mar 17 nicklas 1639       this.categories = categories;
4411 21 Mar 17 nicklas 1640       this.itemsToShare = itemsToShare;
4381 07 Mar 17 nicklas 1641     }
4381 07 Mar 17 nicklas 1642     
4381 07 Mar 17 nicklas 1643     @Override
4381 07 Mar 17 nicklas 1644     AnnotationType create(DbControl dc, String name, JSONObject json)
4381 07 Mar 17 nicklas 1645     {
4381 07 Mar 17 nicklas 1646       Type valueType = Type.valueOf((String)json.get("valueType"));
4388 09 Mar 17 nicklas 1647       AnnotationType at = AnnotationType.getNew(dc, valueType);
4388 09 Mar 17 nicklas 1648       return at;
4381 07 Mar 17 nicklas 1649     }
4388 09 Mar 17 nicklas 1650
4782 25 Apr 18 nicklas 1651     @SuppressWarnings("unchecked")
4388 09 Mar 17 nicklas 1652     @Override
4388 09 Mar 17 nicklas 1653     void update(DbControl dc, AnnotationType item, String name, JSONObject json) 
4388 09 Mar 17 nicklas 1654     {
4388 09 Mar 17 nicklas 1655       super.update(dc, item, name, json);
4388 09 Mar 17 nicklas 1656       
4390 09 Mar 17 nicklas 1657       if (Boolean.TRUE.equals(json.get("projectSpecificValues")))
4390 09 Mar 17 nicklas 1658       {
4390 09 Mar 17 nicklas 1659         // Enable project-specific annotations
4390 09 Mar 17 nicklas 1660         // We can always go from false->true but 
4390 09 Mar 17 nicklas 1661         // not the other way around
4390 09 Mar 17 nicklas 1662         item.setProjectAnnotations(true);
4390 09 Mar 17 nicklas 1663       }
4390 09 Mar 17 nicklas 1664       
4388 09 Mar 17 nicklas 1665       // Item type
4388 09 Mar 17 nicklas 1666       Item itemType = Item.valueOf((String)json.get("itemType"));
4388 09 Mar 17 nicklas 1667       item.enableForItem(itemType);
4388 09 Mar 17 nicklas 1668       
4388 09 Mar 17 nicklas 1669       // Enumeration
4782 25 Apr 18 nicklas 1670       List<?> jsonEnum = (JSONArray)json.get("enumeration");
4388 09 Mar 17 nicklas 1671       if (jsonEnum != null && jsonEnum.size() > 0)
4388 09 Mar 17 nicklas 1672       {
4782 25 Apr 18 nicklas 1673         if (item.getValueType().isNumerical())
4782 25 Apr 18 nicklas 1674         {
4782 25 Apr 18 nicklas 1675           // Numbers are always LONG or DOUBLE in JSON so we way need to convert
4782 25 Apr 18 nicklas 1676           // to INT or FLOAT
4782 25 Apr 18 nicklas 1677           // NOTE! DATE and TIMESTAMP also need conversion from STRING but 
4782 25 Apr 18 nicklas 1678           // we don't have any enumerations of those types so we let it be for now...
4782 25 Apr 18 nicklas 1679           jsonEnum = convertNumbers((List<Number>)jsonEnum, item.getValueType());
4782 25 Apr 18 nicklas 1680         }
4388 09 Mar 17 nicklas 1681         item.setEnumeration(true);
4388 09 Mar 17 nicklas 1682         item.setValues(jsonEnum);
4388 09 Mar 17 nicklas 1683         item.setDisplayAsList(false);
4388 09 Mar 17 nicklas 1684       }
4388 09 Mar 17 nicklas 1685       else
4388 09 Mar 17 nicklas 1686       {
4388 09 Mar 17 nicklas 1687         item.setEnumeration(false);
4388 09 Mar 17 nicklas 1688       }
4411 21 Mar 17 nicklas 1689       
4463 21 Apr 17 nicklas 1690       // Unit
4463 21 Apr 17 nicklas 1691       JSONObject jsonUnit = (JSONObject)json.get("unit");
4463 21 Apr 17 nicklas 1692       if (jsonUnit != null)
4463 21 Apr 17 nicklas 1693       {
4463 21 Apr 17 nicklas 1694         String quantity = (String)jsonUnit.get("quantity");
4463 21 Apr 17 nicklas 1695         String symbol = (String)jsonUnit.get("symbol");
4463 21 Apr 17 nicklas 1696         
4463 21 Apr 17 nicklas 1697         Unit unit = UnitUtil.getUnit(dc, quantity, symbol);
4463 21 Apr 17 nicklas 1698         if (unit == null) throw new ItemNotFoundException("Unit["+quantity+"("+symbol+")]");
4463 21 Apr 17 nicklas 1699         item.setDefaultUnit(unit);
4463 21 Apr 17 nicklas 1700       }
4463 21 Apr 17 nicklas 1701       
4411 21 Mar 17 nicklas 1702       // Categories
4411 21 Mar 17 nicklas 1703       String subtype = (String)json.get("subtype"); // subtype == AnnotationTypeCategory
4411 21 Mar 17 nicklas 1704       if (subtype != null)
4411 21 Mar 17 nicklas 1705       {
4411 21 Mar 17 nicklas 1706         AnnotationTypeCategory category = categories.get(subtype);
4411 21 Mar 17 nicklas 1707         if (category == null)
4411 21 Mar 17 nicklas 1708         {
4411 21 Mar 17 nicklas 1709           category = AnnotationTypeCategory.getNew(dc);
4411 21 Mar 17 nicklas 1710           category.setName(subtype);
4411 21 Mar 17 nicklas 1711           category.setProjectKey(null);
4411 21 Mar 17 nicklas 1712           dc.saveItem(category);
4411 21 Mar 17 nicklas 1713           categories.put(subtype, category);
4411 21 Mar 17 nicklas 1714         }
4411 21 Mar 17 nicklas 1715         item.addCategory(category);
4411 21 Mar 17 nicklas 1716         itemsToShare.add(category);
4411 21 Mar 17 nicklas 1717       }
4388 09 Mar 17 nicklas 1718     }
4782 25 Apr 18 nicklas 1719     
4782 25 Apr 18 nicklas 1720     private List<Number> convertNumbers(List<Number> numbers, Type valueType)
4782 25 Apr 18 nicklas 1721     {
4782 25 Apr 18 nicklas 1722       List<Number> result = new ArrayList<Number>(numbers.size());
4782 25 Apr 18 nicklas 1723       for (Number n : numbers)
4782 25 Apr 18 nicklas 1724       {
4782 25 Apr 18 nicklas 1725         result.add(valueType.convertNumber(n));
4782 25 Apr 18 nicklas 1726       }
4782 25 Apr 18 nicklas 1727       return result;
4782 25 Apr 18 nicklas 1728     }
4411 21 Mar 17 nicklas 1729   }
4411 21 Mar 17 nicklas 1730   
4411 21 Mar 17 nicklas 1731   class DataFileTypeImporter
4411 21 Mar 17 nicklas 1732     extends ItemImporter<DataFileType>
4411 21 Mar 17 nicklas 1733   {
4381 07 Mar 17 nicklas 1734     
4411 21 Mar 17 nicklas 1735     private final Map<String, ItemSubtype> subtypes;
4388 09 Mar 17 nicklas 1736     
4411 21 Mar 17 nicklas 1737     DataFileTypeImporter(Map<String, DataFileType> namedItems, Map<String, ItemSubtype> subtypes)
4411 21 Mar 17 nicklas 1738     {
5189 12 Dec 18 nicklas 1739       super(DataFileType.getQuery(), null, namedItems, true);
4411 21 Mar 17 nicklas 1740       this.subtypes = subtypes;
4411 21 Mar 17 nicklas 1741     }
4411 21 Mar 17 nicklas 1742     
4411 21 Mar 17 nicklas 1743     @Override
4411 21 Mar 17 nicklas 1744     DataFileType create(DbControl dc, String name, JSONObject json)
4411 21 Mar 17 nicklas 1745     {
4411 21 Mar 17 nicklas 1746       Item itemType = Item.valueOf((String)json.get("itemType"));
4411 21 Mar 17 nicklas 1747       String externalId = (String)json.get("externalId");
4411 21 Mar 17 nicklas 1748       DataFileType dft = DataFileType.getNew(dc, externalId, itemType);
4411 21 Mar 17 nicklas 1749       return dft;
4411 21 Mar 17 nicklas 1750     }
4411 21 Mar 17 nicklas 1751
4411 21 Mar 17 nicklas 1752     @Override
4411 21 Mar 17 nicklas 1753     void update(DbControl dc, DataFileType item, String name, JSONObject json) 
4411 21 Mar 17 nicklas 1754     {
4411 21 Mar 17 nicklas 1755       super.update(dc, item, name, json);
4411 21 Mar 17 nicklas 1756       
4411 21 Mar 17 nicklas 1757       item.setExtension((String)json.get("extension"));
4411 21 Mar 17 nicklas 1758       
4411 21 Mar 17 nicklas 1759       // Generic subtype for files
4411 21 Mar 17 nicklas 1760       ItemSubtype subtype = getItemSubtype(subtypes, (String)json.get("genericType"));
4411 21 Mar 17 nicklas 1761       if (subtype != null) item.setGenericType(subtype);
4411 21 Mar 17 nicklas 1762       
4411 21 Mar 17 nicklas 1763       // Subtype for some other item type that is linked with this file type
4411 21 Mar 17 nicklas 1764       // For example: MergedSequences --> FASTQ files
4411 21 Mar 17 nicklas 1765       subtype = subtypes.get((String)json.get("subtype"));
4411 21 Mar 17 nicklas 1766       if (subtype != null)
4411 21 Mar 17 nicklas 1767       {
4411 21 Mar 17 nicklas 1768         ItemSubtypeFileType isft = subtype.getAssociatedDataFileType(item, true);
4411 21 Mar 17 nicklas 1769         isft.setAllowMultiple(Boolean.TRUE.equals(json.get("allowMultiple")));
4411 21 Mar 17 nicklas 1770         isft.setRequired(Boolean.TRUE.equals(json.get("required")));
4411 21 Mar 17 nicklas 1771       }
4411 21 Mar 17 nicklas 1772       
4411 21 Mar 17 nicklas 1773       String platform = (String)json.get("platform");
4411 21 Mar 17 nicklas 1774       String variant = (String)json.get("variant");
4411 21 Mar 17 nicklas 1775       if (platform != null)
4411 21 Mar 17 nicklas 1776       {
4411 21 Mar 17 nicklas 1777         Platform p = null;
4411 21 Mar 17 nicklas 1778         PlatformVariant pv = null;
4411 21 Mar 17 nicklas 1779         if (variant != null)
4411 21 Mar 17 nicklas 1780         {
4411 21 Mar 17 nicklas 1781           pv = PlatformVariant.getByExternalId(dc, variant);
4411 21 Mar 17 nicklas 1782           p = pv.getPlatform();
4411 21 Mar 17 nicklas 1783         }
4411 21 Mar 17 nicklas 1784         else
4411 21 Mar 17 nicklas 1785         {
4411 21 Mar 17 nicklas 1786           p = Platform.getByExternalId(dc, platform);
4411 21 Mar 17 nicklas 1787         }
4411 21 Mar 17 nicklas 1788         
4411 21 Mar 17 nicklas 1789         PlatformFileType pft = p.getFileType(item, pv, true);
4411 21 Mar 17 nicklas 1790         pft.setAllowMultiple(Boolean.TRUE.equals(json.get("allowMultiple")));
4411 21 Mar 17 nicklas 1791         pft.setRequired(Boolean.TRUE.equals(json.get("required")));
4411 21 Mar 17 nicklas 1792       }
4411 21 Mar 17 nicklas 1793     }
4381 07 Mar 17 nicklas 1794   }
4445 06 Apr 17 nicklas 1795   
4445 06 Apr 17 nicklas 1796   /**
6784 02 Aug 22 nicklas 1797     Useful data about annotation types that are used in the import.
6784 02 Aug 22 nicklas 1798   */
6784 02 Aug 22 nicklas 1799   static class ImportedAnnotationType
6784 02 Aug 22 nicklas 1800   {
6784 02 Aug 22 nicklas 1801     final AnnotationType annotationType;
6784 02 Aug 22 nicklas 1802     boolean isNewAnnotationType;
6784 02 Aug 22 nicklas 1803     private Set<Integer> projects;
6784 02 Aug 22 nicklas 1804     
6784 02 Aug 22 nicklas 1805     public ImportedAnnotationType(AnnotationType at) 
6784 02 Aug 22 nicklas 1806     {
6784 02 Aug 22 nicklas 1807       this.annotationType = at;
6784 02 Aug 22 nicklas 1808     }
6784 02 Aug 22 nicklas 1809   }
6784 02 Aug 22 nicklas 1810   
6784 02 Aug 22 nicklas 1811   /**
4445 06 Apr 17 nicklas 1812     Collect things we want to survive a pause and/or shutdown.
4445 06 Apr 17 nicklas 1813   */
4445 06 Apr 17 nicklas 1814   static class ImportState
4445 06 Apr 17 nicklas 1815     implements Serializable
4445 06 Apr 17 nicklas 1816   {
4445 06 Apr 17 nicklas 1817     private static final long serialVersionUID = -5971262435711773460L;
4445 06 Apr 17 nicklas 1818     
4445 06 Apr 17 nicklas 1819     int numFiles;
4445 06 Apr 17 nicklas 1820     long totalSize;
4445 06 Apr 17 nicklas 1821     int numItems;
5189 12 Dec 18 nicklas 1822     int numNotFound;
4445 06 Apr 17 nicklas 1823     int numItemsCreated;
4445 06 Apr 17 nicklas 1824     int numAnnotations;
6784 02 Aug 22 nicklas 1825     int numDefaultConverted;
4445 06 Apr 17 nicklas 1826     
4445 06 Apr 17 nicklas 1827     float percent;
4445 06 Apr 17 nicklas 1828     String message;
4445 06 Apr 17 nicklas 1829     
4447 07 Apr 17 nicklas 1830     int firstFileNo;
4447 07 Apr 17 nicklas 1831     
4445 06 Apr 17 nicklas 1832     void sendProgress(ProgressReporter progress)
4445 06 Apr 17 nicklas 1833     {
4445 06 Apr 17 nicklas 1834       progress.display((int)percent, message);
4445 06 Apr 17 nicklas 1835     }
4445 06 Apr 17 nicklas 1836     
4445 06 Apr 17 nicklas 1837   }
4366 27 Feb 17 nicklas 1838 }