1326 |
29 Mar 11 |
nicklas |
1 |
package net.sf.basedb.reggie.dao; |
1326 |
29 Mar 11 |
nicklas |
2 |
|
4883 |
04 Jul 18 |
nicklas |
3 |
import java.util.ArrayList; |
7269 |
26 Jun 23 |
nicklas |
4 |
import java.util.Collections; |
3764 |
22 Feb 16 |
nicklas |
5 |
import java.util.Comparator; |
5423 |
13 May 19 |
nicklas |
6 |
import java.util.Date; |
3503 |
22 Sep 15 |
nicklas |
7 |
import java.util.HashMap; |
4881 |
03 Jul 18 |
nicklas |
8 |
import java.util.List; |
3503 |
22 Sep 15 |
nicklas |
9 |
import java.util.Map; |
7269 |
26 Jun 23 |
nicklas |
10 |
import java.util.Set; |
5777 |
06 Dec 19 |
nicklas |
11 |
import java.util.regex.Matcher; |
5777 |
06 Dec 19 |
nicklas |
12 |
import java.util.regex.Pattern; |
3503 |
22 Sep 15 |
nicklas |
13 |
|
1326 |
29 Mar 11 |
nicklas |
14 |
import org.json.simple.JSONObject; |
1326 |
29 Mar 11 |
nicklas |
15 |
|
1326 |
29 Mar 11 |
nicklas |
16 |
import net.sf.basedb.core.Annotatable; |
1326 |
29 Mar 11 |
nicklas |
17 |
import net.sf.basedb.core.AnnotationType; |
5694 |
31 Oct 19 |
nicklas |
18 |
import net.sf.basedb.core.AnyToAny; |
4897 |
10 Jul 18 |
nicklas |
19 |
import net.sf.basedb.core.BasicItem; |
3503 |
22 Sep 15 |
nicklas |
20 |
import net.sf.basedb.core.BioMaterial; |
5423 |
13 May 19 |
nicklas |
21 |
import net.sf.basedb.core.ChangeHistory; |
1326 |
29 Mar 11 |
nicklas |
22 |
import net.sf.basedb.core.DbControl; |
3503 |
22 Sep 15 |
nicklas |
23 |
import net.sf.basedb.core.DerivedBioAssay; |
5694 |
31 Oct 19 |
nicklas |
24 |
import net.sf.basedb.core.File; |
4897 |
10 Jul 18 |
nicklas |
25 |
import net.sf.basedb.core.Include; |
4881 |
03 Jul 18 |
nicklas |
26 |
import net.sf.basedb.core.InvalidDataException; |
4897 |
10 Jul 18 |
nicklas |
27 |
import net.sf.basedb.core.Item; |
1821 |
06 Feb 13 |
nicklas |
28 |
import net.sf.basedb.core.ItemNotFoundException; |
4881 |
03 Jul 18 |
nicklas |
29 |
import net.sf.basedb.core.ItemQuery; |
3503 |
22 Sep 15 |
nicklas |
30 |
import net.sf.basedb.core.ItemSubtype; |
3503 |
22 Sep 15 |
nicklas |
31 |
import net.sf.basedb.core.MeasuredBioMaterial; |
1326 |
29 Mar 11 |
nicklas |
32 |
import net.sf.basedb.core.Nameable; |
4897 |
10 Jul 18 |
nicklas |
33 |
import net.sf.basedb.core.Ownable; |
1821 |
06 Feb 13 |
nicklas |
34 |
import net.sf.basedb.core.PermissionDeniedException; |
3503 |
22 Sep 15 |
nicklas |
35 |
import net.sf.basedb.core.RawBioAssay; |
5423 |
13 May 19 |
nicklas |
36 |
import net.sf.basedb.core.Registered; |
4897 |
10 Jul 18 |
nicklas |
37 |
import net.sf.basedb.core.User; |
7269 |
26 Jun 23 |
nicklas |
38 |
import net.sf.basedb.core.SyncFilter.SourceItemTransform; |
5423 |
13 May 19 |
nicklas |
39 |
import net.sf.basedb.core.log.ChangeType; |
4883 |
04 Jul 18 |
nicklas |
40 |
import net.sf.basedb.core.query.Expressions; |
4883 |
04 Jul 18 |
nicklas |
41 |
import net.sf.basedb.core.query.Hql; |
7269 |
26 Jun 23 |
nicklas |
42 |
import net.sf.basedb.core.query.IdListRestriction; |
4883 |
04 Jul 18 |
nicklas |
43 |
import net.sf.basedb.core.query.Orders; |
4883 |
04 Jul 18 |
nicklas |
44 |
import net.sf.basedb.core.query.Restrictions; |
3166 |
05 Mar 15 |
nicklas |
45 |
import net.sf.basedb.core.snapshot.SnapshotManager; |
1326 |
29 Mar 11 |
nicklas |
46 |
import net.sf.basedb.reggie.JsonUtil; |
5423 |
13 May 19 |
nicklas |
47 |
import net.sf.basedb.reggie.Reggie; |
4881 |
03 Jul 18 |
nicklas |
48 |
import net.sf.basedb.reggie.ReservedItems; |
1333 |
05 Apr 11 |
nicklas |
49 |
import net.sf.basedb.reggie.converter.IdentityConverter; |
1333 |
05 Apr 11 |
nicklas |
50 |
import net.sf.basedb.reggie.converter.ValueConverter; |
4883 |
04 Jul 18 |
nicklas |
51 |
import net.sf.basedb.util.MD5; |
4881 |
03 Jul 18 |
nicklas |
52 |
import net.sf.basedb.util.Values; |
7269 |
26 Jun 23 |
nicklas |
53 |
import net.sf.basedb.util.listable.ListableUtil; |
7269 |
26 Jun 23 |
nicklas |
54 |
import net.sf.basedb.util.listable.RestrictionTransformer; |
7269 |
26 Jun 23 |
nicklas |
55 |
import net.sf.basedb.util.listable.SourceItemTransformer; |
7269 |
26 Jun 23 |
nicklas |
56 |
import net.sf.basedb.util.listable.SourceItemTransformerFactory; |
7269 |
26 Jun 23 |
nicklas |
57 |
import net.sf.basedb.util.listable.TransformContext; |
1326 |
29 Mar 11 |
nicklas |
58 |
|
1326 |
29 Mar 11 |
nicklas |
59 |
|
1326 |
29 Mar 11 |
nicklas |
60 |
/** |
1326 |
29 Mar 11 |
nicklas |
An abstract base class for reggie-specific items that have |
1326 |
29 Mar 11 |
nicklas |
been mapped to BASE items using some special rules. This class |
1326 |
29 Mar 11 |
nicklas |
can be used on items that are {@link Annotatable} and |
1326 |
29 Mar 11 |
nicklas |
{@link Nameable}. |
1326 |
29 Mar 11 |
nicklas |
65 |
|
1326 |
29 Mar 11 |
nicklas |
@author nicklas |
1326 |
29 Mar 11 |
nicklas |
@since 1.2 |
1326 |
29 Mar 11 |
nicklas |
68 |
*/ |
1326 |
29 Mar 11 |
nicklas |
69 |
public abstract class ReggieItem<T extends Annotatable & Nameable> |
1326 |
29 Mar 11 |
nicklas |
70 |
{ |
1326 |
29 Mar 11 |
nicklas |
71 |
|
4881 |
03 Jul 18 |
nicklas |
// Reserved child names -- default timeout is 5 minutes |
4883 |
04 Jul 18 |
nicklas |
73 |
private static final ReservedItems<String> RESERVED_NAMES = new ReservedItems<String>(300); |
4881 |
03 Jul 18 |
nicklas |
74 |
|
1326 |
29 Mar 11 |
nicklas |
75 |
private final T item; |
1326 |
29 Mar 11 |
nicklas |
76 |
private JSONObject json; |
3764 |
22 Feb 16 |
nicklas |
77 |
private int indexOrder; |
1326 |
29 Mar 11 |
nicklas |
78 |
|
1326 |
29 Mar 11 |
nicklas |
79 |
protected ReggieItem(T item) |
1326 |
29 Mar 11 |
nicklas |
80 |
{ |
1326 |
29 Mar 11 |
nicklas |
81 |
this.item = item; |
1326 |
29 Mar 11 |
nicklas |
82 |
} |
1326 |
29 Mar 11 |
nicklas |
83 |
|
1326 |
29 Mar 11 |
nicklas |
84 |
/** |
1326 |
29 Mar 11 |
nicklas |
Get the real BASE item that this reggie item represents. |
1326 |
29 Mar 11 |
nicklas |
86 |
*/ |
1326 |
29 Mar 11 |
nicklas |
87 |
public T getItem() |
1326 |
29 Mar 11 |
nicklas |
88 |
{ |
1326 |
29 Mar 11 |
nicklas |
89 |
return item; |
1326 |
29 Mar 11 |
nicklas |
90 |
} |
1326 |
29 Mar 11 |
nicklas |
91 |
|
1326 |
29 Mar 11 |
nicklas |
92 |
/** |
4623 |
17 Nov 17 |
nicklas |
Get the id of the item. |
4623 |
17 Nov 17 |
nicklas |
@since 4.13 |
4623 |
17 Nov 17 |
nicklas |
95 |
*/ |
4623 |
17 Nov 17 |
nicklas |
96 |
public int getId() |
4623 |
17 Nov 17 |
nicklas |
97 |
{ |
4623 |
17 Nov 17 |
nicklas |
98 |
return item.getId(); |
4623 |
17 Nov 17 |
nicklas |
99 |
} |
4623 |
17 Nov 17 |
nicklas |
100 |
|
4623 |
17 Nov 17 |
nicklas |
101 |
/** |
1326 |
29 Mar 11 |
nicklas |
Get the name of the item. |
1326 |
29 Mar 11 |
nicklas |
103 |
*/ |
1326 |
29 Mar 11 |
nicklas |
104 |
public String getName() |
1326 |
29 Mar 11 |
nicklas |
105 |
{ |
1326 |
29 Mar 11 |
nicklas |
106 |
return item.getName(); |
1326 |
29 Mar 11 |
nicklas |
107 |
} |
1326 |
29 Mar 11 |
nicklas |
108 |
|
3764 |
22 Feb 16 |
nicklas |
109 |
/** |
3764 |
22 Feb 16 |
nicklas |
Set an index order value for this item. This can be used to |
3764 |
22 Feb 16 |
nicklas |
sort a list of items with the help of {@link IndexOrderComparator}. |
3764 |
22 Feb 16 |
nicklas |
@since 4.2 |
3764 |
22 Feb 16 |
nicklas |
113 |
*/ |
3764 |
22 Feb 16 |
nicklas |
114 |
public void setIndexOrder(int indexOrder) |
3764 |
22 Feb 16 |
nicklas |
115 |
{ |
3764 |
22 Feb 16 |
nicklas |
116 |
this.indexOrder = indexOrder; |
3764 |
22 Feb 16 |
nicklas |
117 |
} |
3764 |
22 Feb 16 |
nicklas |
118 |
|
3764 |
22 Feb 16 |
nicklas |
119 |
public int getIndexOrder() |
3764 |
22 Feb 16 |
nicklas |
120 |
{ |
3764 |
22 Feb 16 |
nicklas |
121 |
return indexOrder; |
3764 |
22 Feb 16 |
nicklas |
122 |
} |
3764 |
22 Feb 16 |
nicklas |
123 |
|
1521 |
24 Jan 12 |
nicklas |
124 |
@Override |
1521 |
24 Jan 12 |
nicklas |
125 |
public boolean equals(Object o) |
1521 |
24 Jan 12 |
nicklas |
126 |
{ |
1521 |
24 Jan 12 |
nicklas |
127 |
if (this == o) return true; |
1521 |
24 Jan 12 |
nicklas |
128 |
if (o == null || this.getClass() != o.getClass()) return false; |
3572 |
02 Nov 15 |
nicklas |
129 |
return item.equals(((ReggieItem<?>)o).item); |
1521 |
24 Jan 12 |
nicklas |
130 |
} |
1521 |
24 Jan 12 |
nicklas |
131 |
|
1521 |
24 Jan 12 |
nicklas |
132 |
@Override |
1521 |
24 Jan 12 |
nicklas |
133 |
public int hashCode() |
1521 |
24 Jan 12 |
nicklas |
134 |
{ |
1521 |
24 Jan 12 |
nicklas |
135 |
return item.hashCode(); |
1521 |
24 Jan 12 |
nicklas |
136 |
} |
1521 |
24 Jan 12 |
nicklas |
137 |
|
1326 |
29 Mar 11 |
nicklas |
138 |
/** |
1326 |
29 Mar 11 |
nicklas |
Initialize a JSON object with information. Subclasses |
1326 |
29 Mar 11 |
nicklas |
should override this method if the need to put in more |
3571 |
30 Oct 15 |
nicklas |
than the 'id', 'name' or loaded annotations |
3571 |
30 Oct 15 |
nicklas |
(see {@link #loadAnnotations(DbControl, String, Annotationtype, ValueConverter)}) |
1326 |
29 Mar 11 |
nicklas |
143 |
*/ |
1326 |
29 Mar 11 |
nicklas |
144 |
protected void initJSON(JSONObject json) |
1326 |
29 Mar 11 |
nicklas |
145 |
{} |
1326 |
29 Mar 11 |
nicklas |
146 |
|
1326 |
29 Mar 11 |
nicklas |
147 |
/** |
1326 |
29 Mar 11 |
nicklas |
Get the information as a JSON object ready to be sent as an AJAX response. |
1326 |
29 Mar 11 |
nicklas |
149 |
*/ |
1326 |
29 Mar 11 |
nicklas |
150 |
public JSONObject asJSONObject() |
1326 |
29 Mar 11 |
nicklas |
151 |
{ |
1326 |
29 Mar 11 |
nicklas |
152 |
if (json == null) |
1326 |
29 Mar 11 |
nicklas |
153 |
{ |
1326 |
29 Mar 11 |
nicklas |
154 |
json = new JSONObject(); |
1326 |
29 Mar 11 |
nicklas |
155 |
json.put("id", item.getId()); |
1326 |
29 Mar 11 |
nicklas |
156 |
json.put("name", item.getName()); |
1326 |
29 Mar 11 |
nicklas |
157 |
initJSON(json); |
1326 |
29 Mar 11 |
nicklas |
158 |
} |
1326 |
29 Mar 11 |
nicklas |
159 |
return json; |
1326 |
29 Mar 11 |
nicklas |
160 |
} |
1326 |
29 Mar 11 |
nicklas |
161 |
|
1326 |
29 Mar 11 |
nicklas |
162 |
/** |
6218 |
20 Apr 21 |
nicklas |
Get the first non-null value of 'key' and 'keys' |
6218 |
20 Apr 21 |
nicklas |
and put it into 'key'. Return the value. |
6218 |
20 Apr 21 |
nicklas |
@since 4.32 |
6218 |
20 Apr 21 |
nicklas |
166 |
*/ |
6218 |
20 Apr 21 |
nicklas |
167 |
public Object coalesceInto(String key, String... keys) |
6218 |
20 Apr 21 |
nicklas |
168 |
{ |
6218 |
20 Apr 21 |
nicklas |
169 |
JSONObject json = asJSONObject(); |
6218 |
20 Apr 21 |
nicklas |
170 |
Object val = json.get(key); |
6218 |
20 Apr 21 |
nicklas |
171 |
int index = 0; |
6218 |
20 Apr 21 |
nicklas |
172 |
while (val == null && index < keys.length) |
6218 |
20 Apr 21 |
nicklas |
173 |
{ |
6218 |
20 Apr 21 |
nicklas |
174 |
val = json.get(keys[index]); |
6218 |
20 Apr 21 |
nicklas |
175 |
index++; |
6218 |
20 Apr 21 |
nicklas |
176 |
} |
6218 |
20 Apr 21 |
nicklas |
177 |
if (index > 0) json.put(key, val); |
6218 |
20 Apr 21 |
nicklas |
178 |
return val; |
6218 |
20 Apr 21 |
nicklas |
179 |
} |
6218 |
20 Apr 21 |
nicklas |
180 |
|
6218 |
20 Apr 21 |
nicklas |
181 |
/** |
1326 |
29 Mar 11 |
nicklas |
Load annotations for the given annotation type and store the values in |
1326 |
29 Mar 11 |
nicklas |
the given JSON key. Single-valued annotation types are stored as a simple |
1326 |
29 Mar 11 |
nicklas |
key-value pair. Multi-valued annotation types are stored as a |
1326 |
29 Mar 11 |
nicklas |
key-array pair (even if there is only one value in the array). |
1326 |
29 Mar 11 |
nicklas |
186 |
*/ |
3572 |
02 Nov 15 |
nicklas |
187 |
@SuppressWarnings({ "unchecked", "rawtypes" }) |
1610 |
23 Apr 12 |
nicklas |
188 |
public void loadAnnotations(DbControl dc, String jsonKey, Annotationtype annotationType, ValueConverter converter) |
1326 |
29 Mar 11 |
nicklas |
189 |
{ |
1333 |
05 Apr 11 |
nicklas |
190 |
if (converter == null) converter = IdentityConverter.INSTANCE; |
1821 |
06 Feb 13 |
nicklas |
191 |
try |
1326 |
29 Mar 11 |
nicklas |
192 |
{ |
4009 |
23 Jun 16 |
nicklas |
193 |
JSONObject json = asJSONObject(); |
1821 |
06 Feb 13 |
nicklas |
194 |
AnnotationType at = annotationType.load(dc); |
1821 |
06 Feb 13 |
nicklas |
195 |
if (at.getMultiplicity() == 1) |
1821 |
06 Feb 13 |
nicklas |
196 |
{ |
1821 |
06 Feb 13 |
nicklas |
197 |
json.put(jsonKey, converter.convert(annotationType.getAnnotationValue(dc, item))); |
1821 |
06 Feb 13 |
nicklas |
198 |
} |
1821 |
06 Feb 13 |
nicklas |
199 |
else |
1821 |
06 Feb 13 |
nicklas |
200 |
{ |
1821 |
06 Feb 13 |
nicklas |
201 |
json.put(jsonKey, JsonUtil.toArray(annotationType.getAnnotationValues(dc, item), converter)); |
1821 |
06 Feb 13 |
nicklas |
202 |
} |
1326 |
29 Mar 11 |
nicklas |
203 |
} |
1821 |
06 Feb 13 |
nicklas |
204 |
catch (PermissionDeniedException ex) |
1326 |
29 Mar 11 |
nicklas |
205 |
{ |
1821 |
06 Feb 13 |
nicklas |
206 |
json.put(jsonKey, "No permission"); |
1326 |
29 Mar 11 |
nicklas |
207 |
} |
1821 |
06 Feb 13 |
nicklas |
208 |
catch (ItemNotFoundException ex) |
1821 |
06 Feb 13 |
nicklas |
209 |
{ |
1821 |
06 Feb 13 |
nicklas |
210 |
json.put(jsonKey, "Not found (may indicate lack of permission)"); |
1821 |
06 Feb 13 |
nicklas |
211 |
} |
1821 |
06 Feb 13 |
nicklas |
212 |
catch (RuntimeException ex) |
1821 |
06 Feb 13 |
nicklas |
213 |
{ |
1821 |
06 Feb 13 |
nicklas |
214 |
json.put(jsonKey, ex.getMessage()); |
1821 |
06 Feb 13 |
nicklas |
215 |
} |
1326 |
29 Mar 11 |
nicklas |
216 |
} |
1326 |
29 Mar 11 |
nicklas |
217 |
|
1326 |
29 Mar 11 |
nicklas |
218 |
/** |
3166 |
05 Mar 15 |
nicklas |
Load annotations for the given annotation type and store the values in |
3166 |
05 Mar 15 |
nicklas |
the given JSON key. Single-valued annotation types are stored as a simple |
3166 |
05 Mar 15 |
nicklas |
key-value pair. Multi-valued annotation types are stored as a |
3166 |
05 Mar 15 |
nicklas |
key-array pair (even if there is only one value in the array). |
3166 |
05 Mar 15 |
nicklas |
@since 3.2 |
3166 |
05 Mar 15 |
nicklas |
224 |
*/ |
3572 |
02 Nov 15 |
nicklas |
225 |
@SuppressWarnings({ "unchecked", "rawtypes" }) |
3166 |
05 Mar 15 |
nicklas |
226 |
public void loadAnnotations(DbControl dc, SnapshotManager manager, String jsonKey, Annotationtype annotationType, ValueConverter converter) |
3166 |
05 Mar 15 |
nicklas |
227 |
{ |
3166 |
05 Mar 15 |
nicklas |
228 |
if (converter == null) converter = IdentityConverter.INSTANCE; |
3166 |
05 Mar 15 |
nicklas |
229 |
try |
3166 |
05 Mar 15 |
nicklas |
230 |
{ |
4009 |
23 Jun 16 |
nicklas |
231 |
JSONObject json = asJSONObject(); |
3166 |
05 Mar 15 |
nicklas |
232 |
AnnotationType at = annotationType.load(dc); |
3166 |
05 Mar 15 |
nicklas |
233 |
if (at.getMultiplicity() == 1) |
3166 |
05 Mar 15 |
nicklas |
234 |
{ |
3166 |
05 Mar 15 |
nicklas |
235 |
json.put(jsonKey, converter.convert(annotationType.getAnnotationValue(dc, manager, item))); |
3166 |
05 Mar 15 |
nicklas |
236 |
} |
3166 |
05 Mar 15 |
nicklas |
237 |
else |
3166 |
05 Mar 15 |
nicklas |
238 |
{ |
3166 |
05 Mar 15 |
nicklas |
239 |
json.put(jsonKey, JsonUtil.toArray(annotationType.getAnnotationValues(dc, manager, item), converter)); |
3166 |
05 Mar 15 |
nicklas |
240 |
} |
3166 |
05 Mar 15 |
nicklas |
241 |
} |
3166 |
05 Mar 15 |
nicklas |
242 |
catch (PermissionDeniedException ex) |
3166 |
05 Mar 15 |
nicklas |
243 |
{ |
3166 |
05 Mar 15 |
nicklas |
244 |
json.put(jsonKey, "No permission"); |
3166 |
05 Mar 15 |
nicklas |
245 |
} |
3166 |
05 Mar 15 |
nicklas |
246 |
catch (ItemNotFoundException ex) |
3166 |
05 Mar 15 |
nicklas |
247 |
{ |
3166 |
05 Mar 15 |
nicklas |
248 |
json.put(jsonKey, "Not found (may indicate lack of permission)"); |
3166 |
05 Mar 15 |
nicklas |
249 |
} |
3166 |
05 Mar 15 |
nicklas |
250 |
catch (RuntimeException ex) |
3166 |
05 Mar 15 |
nicklas |
251 |
{ |
3166 |
05 Mar 15 |
nicklas |
252 |
json.put(jsonKey, ex.getMessage()); |
3166 |
05 Mar 15 |
nicklas |
253 |
} |
3166 |
05 Mar 15 |
nicklas |
254 |
} |
3166 |
05 Mar 15 |
nicklas |
255 |
|
3166 |
05 Mar 15 |
nicklas |
256 |
/** |
1326 |
29 Mar 11 |
nicklas |
Manually set an annotation value. Or.. actually simply call |
1326 |
29 Mar 11 |
nicklas |
{@link JSONObject#put(Object, Object)} with the given key and value. |
1326 |
29 Mar 11 |
nicklas |
259 |
*/ |
1326 |
29 Mar 11 |
nicklas |
260 |
public void setAnnotation(String jsonKey, Object value) |
1326 |
29 Mar 11 |
nicklas |
261 |
{ |
1326 |
29 Mar 11 |
nicklas |
262 |
JSONObject json = asJSONObject(); |
1326 |
29 Mar 11 |
nicklas |
263 |
json.put(jsonKey, value); |
1326 |
29 Mar 11 |
nicklas |
264 |
} |
3503 |
22 Sep 15 |
nicklas |
265 |
|
3503 |
22 Sep 15 |
nicklas |
266 |
/** |
4983 |
27 Sep 18 |
nicklas |
Helper method for loading the DO_NOT_USE and DO_NOT_USE_COMMENT annotations |
4983 |
27 Sep 18 |
nicklas |
on items that support it. |
4983 |
27 Sep 18 |
nicklas |
@since 4.20 |
4983 |
27 Sep 18 |
nicklas |
270 |
*/ |
4983 |
27 Sep 18 |
nicklas |
271 |
public void loadDoNotUseAnnotations(DbControl dc, SnapshotManager manager) |
4983 |
27 Sep 18 |
nicklas |
272 |
{ |
4983 |
27 Sep 18 |
nicklas |
273 |
loadAnnotations(dc, manager, "DO_NOT_USE", Annotationtype.DO_NOT_USE, null); |
4983 |
27 Sep 18 |
nicklas |
274 |
loadAnnotations(dc, manager, "DO_NOT_USE_COMMENT", Annotationtype.DO_NOT_USE_COMMENT, null); |
4983 |
27 Sep 18 |
nicklas |
275 |
} |
4983 |
27 Sep 18 |
nicklas |
276 |
|
5789 |
13 Dec 19 |
nicklas |
277 |
|
4983 |
27 Sep 18 |
nicklas |
278 |
/** |
3503 |
22 Sep 15 |
nicklas |
Utility method for finding parent biomaterial of the specified subtypes |
3503 |
22 Sep 15 |
nicklas |
by following the chain of parent items up to biosource. |
3503 |
22 Sep 15 |
nicklas |
The starting point can be either a biomaterial, a raw bioassay or a |
3503 |
22 Sep 15 |
nicklas |
derived bioassay with a link to a parent extract (library). |
3503 |
22 Sep 15 |
nicklas |
@since 3.7 |
3503 |
22 Sep 15 |
nicklas |
284 |
*/ |
3503 |
22 Sep 15 |
nicklas |
285 |
public Map<Subtype, BioMaterial> findParentBioMaterial(DbControl dc, Subtype... subtypes) |
3503 |
22 Sep 15 |
nicklas |
286 |
{ |
3503 |
22 Sep 15 |
nicklas |
287 |
Map<Subtype, BioMaterial> parents = new HashMap<Subtype, BioMaterial>(); |
3503 |
22 Sep 15 |
nicklas |
288 |
for (Subtype sub : subtypes) |
3503 |
22 Sep 15 |
nicklas |
289 |
{ |
3503 |
22 Sep 15 |
nicklas |
290 |
sub.load(dc); |
3503 |
22 Sep 15 |
nicklas |
291 |
parents.put(sub, null); |
3503 |
22 Sep 15 |
nicklas |
292 |
} |
3503 |
22 Sep 15 |
nicklas |
293 |
|
3503 |
22 Sep 15 |
nicklas |
294 |
|
3503 |
22 Sep 15 |
nicklas |
295 |
BioMaterial bm = null; |
3503 |
22 Sep 15 |
nicklas |
296 |
if (item instanceof BioMaterial) |
3503 |
22 Sep 15 |
nicklas |
297 |
{ |
3503 |
22 Sep 15 |
nicklas |
298 |
bm = (BioMaterial)item; |
3503 |
22 Sep 15 |
nicklas |
299 |
} |
3503 |
22 Sep 15 |
nicklas |
300 |
else if (item instanceof RawBioAssay) |
3503 |
22 Sep 15 |
nicklas |
301 |
{ |
3503 |
22 Sep 15 |
nicklas |
302 |
bm = ((RawBioAssay)item).getParentExtract(); |
3503 |
22 Sep 15 |
nicklas |
303 |
} |
3503 |
22 Sep 15 |
nicklas |
304 |
else if (item instanceof DerivedBioAssay) |
3503 |
22 Sep 15 |
nicklas |
305 |
{ |
3503 |
22 Sep 15 |
nicklas |
306 |
bm = ((DerivedBioAssay)item).getExtract(); |
3503 |
22 Sep 15 |
nicklas |
307 |
} |
3503 |
22 Sep 15 |
nicklas |
308 |
|
6075 |
23 Nov 20 |
nicklas |
309 |
int numItems = 0; |
6075 |
23 Nov 20 |
nicklas |
310 |
while (bm != null && numItems < subtypes.length) |
3503 |
22 Sep 15 |
nicklas |
311 |
{ |
3503 |
22 Sep 15 |
nicklas |
312 |
ItemSubtype bmType = bm.getItemSubtype(); |
3503 |
22 Sep 15 |
nicklas |
313 |
|
6946 |
06 Dec 22 |
nicklas |
314 |
if (bmType != null) |
6075 |
23 Nov 20 |
nicklas |
315 |
{ |
6946 |
06 Dec 22 |
nicklas |
316 |
Subtype st = Subtype.get(bmType); |
6946 |
06 Dec 22 |
nicklas |
317 |
if (parents.containsKey(st)) |
6946 |
06 Dec 22 |
nicklas |
318 |
{ |
7007 |
25 Jan 23 |
nicklas |
// Increase the number of items found only if it was a new type |
7007 |
25 Jan 23 |
nicklas |
320 |
if (parents.put(st, bm) == null) numItems++; |
6946 |
06 Dec 22 |
nicklas |
321 |
} |
6075 |
23 Nov 20 |
nicklas |
322 |
} |
3503 |
22 Sep 15 |
nicklas |
323 |
|
3503 |
22 Sep 15 |
nicklas |
324 |
if (bm instanceof MeasuredBioMaterial) |
3503 |
22 Sep 15 |
nicklas |
325 |
{ |
4426 |
27 Mar 17 |
nicklas |
// Get parent, may be null if starting with item derived from external RNA! |
3503 |
22 Sep 15 |
nicklas |
327 |
bm = ((MeasuredBioMaterial)bm).getParent(); |
3503 |
22 Sep 15 |
nicklas |
328 |
} |
3503 |
22 Sep 15 |
nicklas |
329 |
else |
3503 |
22 Sep 15 |
nicklas |
330 |
{ |
3503 |
22 Sep 15 |
nicklas |
// We have reached the patient! |
4426 |
27 Mar 17 |
nicklas |
332 |
bm = null; |
3503 |
22 Sep 15 |
nicklas |
333 |
} |
3503 |
22 Sep 15 |
nicklas |
334 |
} |
3503 |
22 Sep 15 |
nicklas |
335 |
|
3503 |
22 Sep 15 |
nicklas |
336 |
return parents; |
3503 |
22 Sep 15 |
nicklas |
337 |
|
3503 |
22 Sep 15 |
nicklas |
338 |
} |
3503 |
22 Sep 15 |
nicklas |
339 |
|
3764 |
22 Feb 16 |
nicklas |
340 |
/** |
3792 |
18 Mar 16 |
nicklas |
Utility method for finding a single parent item of a given type. |
3792 |
18 Mar 16 |
nicklas |
If you need to find multiple parents use the {@link #findParentBioMaterial(DbControl, Subtype...)} |
3792 |
18 Mar 16 |
nicklas |
since it is more efficient. |
3792 |
18 Mar 16 |
nicklas |
@return The found biomaterial or null if not found |
3792 |
18 Mar 16 |
nicklas |
@since 4.3 |
3792 |
18 Mar 16 |
nicklas |
346 |
*/ |
3792 |
18 Mar 16 |
nicklas |
347 |
public BioMaterial findSingleParent(DbControl dc, Subtype subtype) |
3792 |
18 Mar 16 |
nicklas |
348 |
{ |
3792 |
18 Mar 16 |
nicklas |
349 |
return findParentBioMaterial(dc, subtype).get(subtype); |
3792 |
18 Mar 16 |
nicklas |
350 |
} |
3792 |
18 Mar 16 |
nicklas |
351 |
|
7269 |
26 Jun 23 |
nicklas |
352 |
/** |
7269 |
26 Jun 23 |
nicklas |
Create a query for finding child items of the given subtype. |
7269 |
26 Jun 23 |
nicklas |
@since 4.49 |
7269 |
26 Jun 23 |
nicklas |
355 |
*/ |
7269 |
26 Jun 23 |
nicklas |
356 |
public <C extends BasicItem> ItemQuery<C> findChildItems(DbControl dc, Subtype childType) |
7269 |
26 Jun 23 |
nicklas |
357 |
{ |
7269 |
26 Jun 23 |
nicklas |
358 |
SourceItemTransformerFactory stf = ListableUtil.getTransformerFactory(childType.getMainType()); |
7269 |
26 Jun 23 |
nicklas |
359 |
SourceItemTransformer st = stf.create(getItem().getType(), SourceItemTransform.PARENT_TO_CHILD); |
7269 |
26 Jun 23 |
nicklas |
360 |
st = new RestrictionTransformer(st, childType.restriction(dc, null)); |
7269 |
26 Jun 23 |
nicklas |
361 |
TransformContext ctx = new TransformContext(dc); |
7269 |
26 Jun 23 |
nicklas |
362 |
ctx.setInclude(Reggie.INCLUDE_IN_CURRENT_PROJECT); |
7269 |
26 Jun 23 |
nicklas |
363 |
|
7269 |
26 Jun 23 |
nicklas |
364 |
Set<Integer> children = st.transform(ctx, Collections.singleton(getId())); |
7269 |
26 Jun 23 |
nicklas |
365 |
ItemQuery<C> query = childType.getMainType().getQuery(); |
7269 |
26 Jun 23 |
nicklas |
366 |
query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT); |
7269 |
26 Jun 23 |
nicklas |
367 |
query.restrictPermanent(new IdListRestriction(children)); |
7269 |
26 Jun 23 |
nicklas |
368 |
return query; |
7269 |
26 Jun 23 |
nicklas |
369 |
} |
4893 |
09 Jul 18 |
nicklas |
370 |
|
5694 |
31 Oct 19 |
nicklas |
371 |
/** |
5694 |
31 Oct 19 |
nicklas |
Get a file that is linked via an any-to-any link with a known |
5694 |
31 Oct 19 |
nicklas |
name. |
5694 |
31 Oct 19 |
nicklas |
@since 4.24 |
5694 |
31 Oct 19 |
nicklas |
375 |
*/ |
5694 |
31 Oct 19 |
nicklas |
376 |
public File getLinkedFile(DbControl dc, String linkName) |
5694 |
31 Oct 19 |
nicklas |
377 |
{ |
5694 |
31 Oct 19 |
nicklas |
378 |
File file = null; |
5694 |
31 Oct 19 |
nicklas |
379 |
try |
5694 |
31 Oct 19 |
nicklas |
380 |
{ |
5694 |
31 Oct 19 |
nicklas |
381 |
AnyToAny link = AnyToAny.getByName(dc, (BasicItem)getItem(), linkName); |
5694 |
31 Oct 19 |
nicklas |
382 |
if (link.getToType() == Item.FILE) file = (File)link.getTo(); |
5694 |
31 Oct 19 |
nicklas |
383 |
} |
5694 |
31 Oct 19 |
nicklas |
384 |
catch (ItemNotFoundException ex) |
5694 |
31 Oct 19 |
nicklas |
385 |
{} |
5694 |
31 Oct 19 |
nicklas |
386 |
return file; |
5694 |
31 Oct 19 |
nicklas |
387 |
} |
5694 |
31 Oct 19 |
nicklas |
388 |
|
7436 |
15 Nov 23 |
nicklas |
389 |
/** |
7436 |
15 Nov 23 |
nicklas |
Get an item that is linked via an any-to-any link with a known name. |
7436 |
15 Nov 23 |
nicklas |
If the link or item doesn't exists null is returned. |
7436 |
15 Nov 23 |
nicklas |
@since 4.50 |
7436 |
15 Nov 23 |
nicklas |
393 |
*/ |
7436 |
15 Nov 23 |
nicklas |
394 |
@SuppressWarnings("unchecked") |
7436 |
15 Nov 23 |
nicklas |
395 |
public <L extends BasicItem> L getLinkedItem(DbControl dc, String linkName) |
7436 |
15 Nov 23 |
nicklas |
396 |
{ |
7436 |
15 Nov 23 |
nicklas |
397 |
L item = null; |
7436 |
15 Nov 23 |
nicklas |
398 |
try |
7436 |
15 Nov 23 |
nicklas |
399 |
{ |
7436 |
15 Nov 23 |
nicklas |
400 |
AnyToAny link = AnyToAny.getByName(dc, (BasicItem)getItem(), linkName); |
7436 |
15 Nov 23 |
nicklas |
401 |
item = (L)link.getTo(); |
7436 |
15 Nov 23 |
nicklas |
402 |
} |
7436 |
15 Nov 23 |
nicklas |
403 |
catch (ItemNotFoundException ex) |
7436 |
15 Nov 23 |
nicklas |
404 |
{} |
7436 |
15 Nov 23 |
nicklas |
405 |
return item; |
7436 |
15 Nov 23 |
nicklas |
406 |
} |
7436 |
15 Nov 23 |
nicklas |
407 |
|
7436 |
15 Nov 23 |
nicklas |
408 |
|
4893 |
09 Jul 18 |
nicklas |
409 |
protected String getNextChildItemName(DbControl dc, ItemQuery<? extends Nameable> childQuery, String suffix, boolean releaseIfTransactionFails) |
4893 |
09 Jul 18 |
nicklas |
410 |
{ |
4893 |
09 Jul 18 |
nicklas |
411 |
return getNextChildItemName(dc, getName(), childQuery, suffix, releaseIfTransactionFails); |
4893 |
09 Jul 18 |
nicklas |
412 |
} |
4893 |
09 Jul 18 |
nicklas |
413 |
|
3792 |
18 Mar 16 |
nicklas |
414 |
/** |
4881 |
03 Jul 18 |
nicklas |
Generates a new child item name based on existing child items. The given |
4881 |
03 Jul 18 |
nicklas |
query is assumed to return all existing child items. They should all have a |
4881 |
03 Jul 18 |
nicklas |
base name starting with the name of this item + the suffix (separated with a .) |
4881 |
03 Jul 18 |
nicklas |
418 |
|
4881 |
03 Jul 18 |
nicklas |
If no child items exists the first child is given the base name. Subsequent child |
4898 |
10 Jul 18 |
nicklas |
items are indexed with 2, 3, 4, and so on. Exception, if the suffix is an empty string |
4898 |
10 Jul 18 |
nicklas |
the first child item get index = 1. |
6193 |
30 Mar 21 |
nicklas |
422 |
|
6193 |
30 Mar 21 |
nicklas |
If the query is null it is assumed that the parent item is a newly created |
6193 |
30 Mar 21 |
nicklas |
item that can't have any existing children. |
4881 |
03 Jul 18 |
nicklas |
425 |
|
4881 |
03 Jul 18 |
nicklas |
Generated names are reserved for 5 minutes, but can optionally be released |
4881 |
03 Jul 18 |
nicklas |
immediately if the transaction fails. Typically, this parameter should be TRUE |
4881 |
03 Jul 18 |
nicklas |
if the name is generated and a new item is created and saved to the database |
4881 |
03 Jul 18 |
nicklas |
in a single transaction. This will release the reserved name in case the |
4881 |
03 Jul 18 |
nicklas |
transaction fails and should avoid unnecessary "holes" in the child names |
4881 |
03 Jul 18 |
nicklas |
when re-trying the action. |
4881 |
03 Jul 18 |
nicklas |
432 |
|
4881 |
03 Jul 18 |
nicklas |
The parameter should be FALSE if the name is generated in the beginning of a |
4881 |
03 Jul 18 |
nicklas |
multi-step wizard and not saved to the database until the final step in the |
4881 |
03 Jul 18 |
nicklas |
wizard. This may lead to "holes" in the child names if a wizard is not |
4881 |
03 Jul 18 |
nicklas |
completed, but there is no easy solution for that. |
4881 |
03 Jul 18 |
nicklas |
437 |
|
4881 |
03 Jul 18 |
nicklas |
Calling this method more than once within the same transaction |
4881 |
03 Jul 18 |
nicklas |
should create unique names every time. |
4883 |
04 Jul 18 |
nicklas |
@since 4.19 |
4881 |
03 Jul 18 |
nicklas |
441 |
*/ |
4893 |
09 Jul 18 |
nicklas |
442 |
public static String getNextChildItemName(DbControl dc, String parentName, ItemQuery<? extends Nameable> childQuery, String suffix, boolean releaseIfTransactionFails) |
4881 |
03 Jul 18 |
nicklas |
443 |
{ |
4881 |
03 Jul 18 |
nicklas |
444 |
|
4881 |
03 Jul 18 |
nicklas |
// All child item names should start with this string |
4893 |
09 Jul 18 |
nicklas |
446 |
String baseName = parentName+"."+suffix; |
4881 |
03 Jul 18 |
nicklas |
447 |
|
4881 |
03 Jul 18 |
nicklas |
448 |
int maxChildIndex = 0; // The highest index of a child item we can find |
6193 |
30 Mar 21 |
nicklas |
449 |
if (childQuery != null) |
4881 |
03 Jul 18 |
nicklas |
450 |
{ |
6193 |
30 Mar 21 |
nicklas |
451 |
List<? extends Nameable> existingChildren = childQuery.list(dc); |
6193 |
30 Mar 21 |
nicklas |
452 |
for (Nameable child : existingChildren) |
5188 |
07 Dec 18 |
nicklas |
453 |
{ |
6193 |
30 Mar 21 |
nicklas |
454 |
String childName = child.getName(); |
6193 |
30 Mar 21 |
nicklas |
455 |
if (!childName.startsWith(baseName)) |
5188 |
07 Dec 18 |
nicklas |
456 |
{ |
5188 |
07 Dec 18 |
nicklas |
457 |
throw new InvalidDataException("Child item '" + childName + "' of '" + parentName + |
6193 |
30 Mar 21 |
nicklas |
458 |
"' has unexpected name. It should start with '" + baseName + "'."); |
5188 |
07 Dec 18 |
nicklas |
459 |
} |
6193 |
30 Mar 21 |
nicklas |
460 |
|
6193 |
30 Mar 21 |
nicklas |
461 |
int childIndex = 0; |
6193 |
30 Mar 21 |
nicklas |
462 |
String digits = childName.substring(baseName.length()); |
6193 |
30 Mar 21 |
nicklas |
463 |
|
6193 |
30 Mar 21 |
nicklas |
// The first child has no digits and this string may be empty |
6193 |
30 Mar 21 |
nicklas |
465 |
if (digits.equals("")) |
6193 |
30 Mar 21 |
nicklas |
466 |
{ |
6193 |
30 Mar 21 |
nicklas |
467 |
childIndex = 1; |
6193 |
30 Mar 21 |
nicklas |
468 |
} |
6193 |
30 Mar 21 |
nicklas |
469 |
else |
6193 |
30 Mar 21 |
nicklas |
470 |
{ |
6193 |
30 Mar 21 |
nicklas |
// Oyherwise, the rest of the name should be digits |
6193 |
30 Mar 21 |
nicklas |
472 |
if (!digits.matches("\\d+")) |
6193 |
30 Mar 21 |
nicklas |
473 |
{ |
6193 |
30 Mar 21 |
nicklas |
474 |
throw new InvalidDataException("Child item '" + childName + "' of '" + parentName + |
6193 |
30 Mar 21 |
nicklas |
475 |
"' has unexpected name. Digits expected but found '" + digits + "'."); |
6193 |
30 Mar 21 |
nicklas |
476 |
} |
6193 |
30 Mar 21 |
nicklas |
477 |
childIndex = Values.getInt(digits); |
6193 |
30 Mar 21 |
nicklas |
478 |
} |
6193 |
30 Mar 21 |
nicklas |
479 |
if (childIndex > maxChildIndex) maxChildIndex = childIndex; |
4881 |
03 Jul 18 |
nicklas |
480 |
} |
4881 |
03 Jul 18 |
nicklas |
481 |
} |
4881 |
03 Jul 18 |
nicklas |
482 |
|
4881 |
03 Jul 18 |
nicklas |
483 |
int nextChildIndex = maxChildIndex+1; |
4898 |
10 Jul 18 |
nicklas |
484 |
String childName = baseName + (nextChildIndex == 1 && suffix.length() > 0 ? "" : nextChildIndex); |
4881 |
03 Jul 18 |
nicklas |
485 |
|
4881 |
03 Jul 18 |
nicklas |
486 |
int numIterations = 0; |
4889 |
06 Jul 18 |
nicklas |
487 |
while (!RESERVED_NAMES.reserve(dc, childName)) |
4881 |
03 Jul 18 |
nicklas |
488 |
{ |
4881 |
03 Jul 18 |
nicklas |
489 |
nextChildIndex++; |
4881 |
03 Jul 18 |
nicklas |
490 |
childName = baseName+nextChildIndex; |
4881 |
03 Jul 18 |
nicklas |
491 |
numIterations++; |
4881 |
03 Jul 18 |
nicklas |
492 |
if (numIterations == 100) |
4881 |
03 Jul 18 |
nicklas |
493 |
{ |
4893 |
09 Jul 18 |
nicklas |
494 |
throw new RuntimeException("Failed to generate a child name for '" + parentName + |
4881 |
03 Jul 18 |
nicklas |
495 |
"' after 100 tries. Last tested child name was '" + childName + "'."); |
4881 |
03 Jul 18 |
nicklas |
496 |
} |
4881 |
03 Jul 18 |
nicklas |
497 |
} |
4881 |
03 Jul 18 |
nicklas |
498 |
|
4881 |
03 Jul 18 |
nicklas |
499 |
if (releaseIfTransactionFails) |
4881 |
03 Jul 18 |
nicklas |
500 |
{ |
4883 |
04 Jul 18 |
nicklas |
501 |
RESERVED_NAMES.releaseIfTransactionFails(dc, childName); |
4881 |
03 Jul 18 |
nicklas |
502 |
} |
4881 |
03 Jul 18 |
nicklas |
503 |
|
4881 |
03 Jul 18 |
nicklas |
504 |
return childName; |
4881 |
03 Jul 18 |
nicklas |
505 |
} |
4881 |
03 Jul 18 |
nicklas |
506 |
|
4883 |
04 Jul 18 |
nicklas |
507 |
/** |
4883 |
04 Jul 18 |
nicklas |
Creates a single name. |
4883 |
04 Jul 18 |
nicklas |
@see #getNextItemNames(DbControl, int, ItemQuery, String, int, boolean) |
4883 |
04 Jul 18 |
nicklas |
510 |
*/ |
4893 |
09 Jul 18 |
nicklas |
511 |
public static String getNextItemName(DbControl dc, ItemQuery<? extends Nameable> query, String prefix, int numDigitsInName, boolean releaseIfTransactionFails) |
4883 |
04 Jul 18 |
nicklas |
512 |
{ |
4883 |
04 Jul 18 |
nicklas |
513 |
return getNextItemNames(dc, 1, query, prefix, numDigitsInName, releaseIfTransactionFails).get(0); |
4883 |
04 Jul 18 |
nicklas |
514 |
} |
4881 |
03 Jul 18 |
nicklas |
515 |
|
4881 |
03 Jul 18 |
nicklas |
516 |
/** |
4883 |
04 Jul 18 |
nicklas |
Generates one or more new item names based on existing items. The given query is assumed to return |
4883 |
04 Jul 18 |
nicklas |
all existing items that should be considered. This method will add a filter on the |
4883 |
04 Jul 18 |
nicklas |
name column that must start with the given "prefix" and then be followd by only digits. |
4883 |
04 Jul 18 |
nicklas |
This method will also re-sort the query in descending name order (so that the first |
4883 |
04 Jul 18 |
nicklas |
item is the one with the highest index). |
4883 |
04 Jul 18 |
nicklas |
522 |
|
4883 |
04 Jul 18 |
nicklas |
Generated names are reserved for 5 minutes, but can optionally be released |
4883 |
04 Jul 18 |
nicklas |
immediately if the transaction fails. Typically, this parameter should be TRUE |
4883 |
04 Jul 18 |
nicklas |
if the name is generated and a new item is created and saved to the database |
4883 |
04 Jul 18 |
nicklas |
in a single transaction. This will release the reserved name in case the |
4883 |
04 Jul 18 |
nicklas |
transaction fails and should avoid unnecessary "holes" among the names when |
4883 |
04 Jul 18 |
nicklas |
re-trying the action. |
4883 |
04 Jul 18 |
nicklas |
529 |
|
4883 |
04 Jul 18 |
nicklas |
The parameter should be FALSE if the name is generated in the beginning of a |
4883 |
04 Jul 18 |
nicklas |
multi-step wizard and not saved to the database until the final step in the |
4883 |
04 Jul 18 |
nicklas |
wizard. This may lead to "holes" in the sequence of names if a wizard is not |
4883 |
04 Jul 18 |
nicklas |
completed, but there is no easy solution for that. |
4902 |
10 Jul 18 |
nicklas |
534 |
|
4883 |
04 Jul 18 |
nicklas |
Calling this method more than once within the same transaction |
4883 |
04 Jul 18 |
nicklas |
should create unique names every time. |
4883 |
04 Jul 18 |
nicklas |
537 |
|
4883 |
04 Jul 18 |
nicklas |
@since 4.19 |
4883 |
04 Jul 18 |
nicklas |
539 |
*/ |
4883 |
04 Jul 18 |
nicklas |
540 |
protected static List<String> getNextItemNames(DbControl dc, int numNames, ItemQuery<? extends Nameable> query, String prefix, int numDigitsInName, boolean releaseIfTransactionFails) |
4883 |
04 Jul 18 |
nicklas |
541 |
{ |
4883 |
04 Jul 18 |
nicklas |
// Add filter to the query and re-sort on name in descending order |
4883 |
04 Jul 18 |
nicklas |
543 |
query.restrict(Restrictions.rlike(Hql.property("name"), Expressions.string("^"+prefix + "[0-9]+$"))); |
4883 |
04 Jul 18 |
nicklas |
544 |
query.order(Orders.desc(Hql.property("name"))); |
4883 |
04 Jul 18 |
nicklas |
545 |
query.setMaxResults(1); |
4902 |
10 Jul 18 |
nicklas |
546 |
query.include(Include.ALL); |
4883 |
04 Jul 18 |
nicklas |
547 |
|
4883 |
04 Jul 18 |
nicklas |
548 |
List<? extends Nameable> result = query.list(dc); |
4883 |
04 Jul 18 |
nicklas |
549 |
int nextIndex = 1; |
4883 |
04 Jul 18 |
nicklas |
550 |
if (result.size() > 0) |
4883 |
04 Jul 18 |
nicklas |
551 |
{ |
4883 |
04 Jul 18 |
nicklas |
552 |
String lastItemName = result.get(0).getName(); |
4883 |
04 Jul 18 |
nicklas |
553 |
String digits = lastItemName.substring(prefix.length()); |
4883 |
04 Jul 18 |
nicklas |
554 |
if (!digits.matches("\\d+")) |
4883 |
04 Jul 18 |
nicklas |
555 |
{ |
4883 |
04 Jul 18 |
nicklas |
556 |
throw new InvalidDataException("Item '" + lastItemName + |
4883 |
04 Jul 18 |
nicklas |
557 |
"' has unexpected name. Digits expected after '"+prefix + "' but found '" + digits + "'."); |
4883 |
04 Jul 18 |
nicklas |
558 |
} |
4883 |
04 Jul 18 |
nicklas |
559 |
nextIndex = Values.getInt(digits)+1; |
4883 |
04 Jul 18 |
nicklas |
560 |
} |
4883 |
04 Jul 18 |
nicklas |
561 |
|
4883 |
04 Jul 18 |
nicklas |
562 |
int numIterations = 0; |
4883 |
04 Jul 18 |
nicklas |
// This will hold the generated names |
4883 |
04 Jul 18 |
nicklas |
564 |
List<String> nextNames = new ArrayList<>(numNames); |
4883 |
04 Jul 18 |
nicklas |
565 |
while (true) |
4883 |
04 Jul 18 |
nicklas |
566 |
{ |
4883 |
04 Jul 18 |
nicklas |
567 |
String nextName = prefix + MD5.leftPad(Integer.toString(nextIndex), '0', numDigitsInName); |
4889 |
06 Jul 18 |
nicklas |
568 |
if (RESERVED_NAMES.reserve(dc, nextName)) |
4883 |
04 Jul 18 |
nicklas |
569 |
{ |
4883 |
04 Jul 18 |
nicklas |
570 |
if (releaseIfTransactionFails) |
4883 |
04 Jul 18 |
nicklas |
571 |
{ |
4883 |
04 Jul 18 |
nicklas |
572 |
RESERVED_NAMES.releaseIfTransactionFails(dc, nextName); |
4883 |
04 Jul 18 |
nicklas |
573 |
} |
4883 |
04 Jul 18 |
nicklas |
574 |
nextNames.add(nextName); |
4883 |
04 Jul 18 |
nicklas |
575 |
if (nextNames.size() == numNames) break; // We have enough names |
4883 |
04 Jul 18 |
nicklas |
576 |
numIterations = 0; |
4883 |
04 Jul 18 |
nicklas |
577 |
} |
4883 |
04 Jul 18 |
nicklas |
578 |
nextIndex++; |
4883 |
04 Jul 18 |
nicklas |
579 |
numIterations++; |
4883 |
04 Jul 18 |
nicklas |
580 |
if (numIterations == 100) |
4883 |
04 Jul 18 |
nicklas |
581 |
{ |
4883 |
04 Jul 18 |
nicklas |
582 |
throw new RuntimeException("Failed to generate a name for '" + prefix + |
4883 |
04 Jul 18 |
nicklas |
583 |
"' after 100 tries. Last tested name was '" + nextName + "'."); |
4883 |
04 Jul 18 |
nicklas |
584 |
} |
4883 |
04 Jul 18 |
nicklas |
585 |
} |
4889 |
06 Jul 18 |
nicklas |
586 |
|
4883 |
04 Jul 18 |
nicklas |
587 |
return nextNames; |
4883 |
04 Jul 18 |
nicklas |
588 |
} |
4883 |
04 Jul 18 |
nicklas |
589 |
|
4883 |
04 Jul 18 |
nicklas |
590 |
/** |
4902 |
10 Jul 18 |
nicklas |
Generate the next auto-generated external ID. This method will search all items |
4902 |
10 Jul 18 |
nicklas |
starting with the given prefix and find the one with the highest numeric suffix. |
4902 |
10 Jul 18 |
nicklas |
The returned string is the found index + 1. This method uses the |
4902 |
10 Jul 18 |
nicklas |
{@link ReservedItems} to make sure that the same id is not generated |
4902 |
10 Jul 18 |
nicklas |
twice. |
4897 |
10 Jul 18 |
nicklas |
@since 4.19 |
4902 |
10 Jul 18 |
nicklas |
597 |
*/ |
4902 |
10 Jul 18 |
nicklas |
598 |
public static String getNextExternalId(DbControl dc, ItemQuery<? extends BioMaterial> query, String prefix) |
4902 |
10 Jul 18 |
nicklas |
599 |
{ |
4902 |
10 Jul 18 |
nicklas |
600 |
query.restrict(Restrictions.rlike(Hql.property("externalId"), Expressions.string("^" + prefix + "[0-9]+$"))); |
4902 |
10 Jul 18 |
nicklas |
601 |
query.order(Orders.desc(Hql.property("externalId"))); |
4902 |
10 Jul 18 |
nicklas |
602 |
query.setMaxResults(1); |
4902 |
10 Jul 18 |
nicklas |
603 |
query.include(Include.ALL); |
4902 |
10 Jul 18 |
nicklas |
604 |
|
4902 |
10 Jul 18 |
nicklas |
605 |
|
4902 |
10 Jul 18 |
nicklas |
606 |
List<? extends BioMaterial> result = query.list(dc); |
4902 |
10 Jul 18 |
nicklas |
607 |
int nextIndex = 1; |
4902 |
10 Jul 18 |
nicklas |
608 |
if (result.size() > 0) |
4902 |
10 Jul 18 |
nicklas |
609 |
{ |
4902 |
10 Jul 18 |
nicklas |
610 |
String lastItemId = result.get(0).getExternalId(); |
4902 |
10 Jul 18 |
nicklas |
611 |
String digits = lastItemId.substring(prefix.length()); |
4902 |
10 Jul 18 |
nicklas |
612 |
if (!digits.matches("\\d+")) |
4902 |
10 Jul 18 |
nicklas |
613 |
{ |
4902 |
10 Jul 18 |
nicklas |
614 |
throw new InvalidDataException("Item '" + lastItemId + |
4902 |
10 Jul 18 |
nicklas |
615 |
"' has unexpected name. Digits expected after '"+prefix + "' but found '" + digits + "'."); |
4902 |
10 Jul 18 |
nicklas |
616 |
} |
4902 |
10 Jul 18 |
nicklas |
617 |
nextIndex = Values.getInt(digits)+1; |
4902 |
10 Jul 18 |
nicklas |
618 |
} |
4902 |
10 Jul 18 |
nicklas |
619 |
|
4902 |
10 Jul 18 |
nicklas |
620 |
int numDigitsInName = 6; |
4902 |
10 Jul 18 |
nicklas |
621 |
int numIterations = 0; |
4902 |
10 Jul 18 |
nicklas |
// This will hold the generated names |
4902 |
10 Jul 18 |
nicklas |
623 |
String nextExternalId; |
4902 |
10 Jul 18 |
nicklas |
624 |
while (true) |
4902 |
10 Jul 18 |
nicklas |
625 |
{ |
4902 |
10 Jul 18 |
nicklas |
626 |
nextExternalId = prefix + MD5.leftPad(Integer.toString(nextIndex), '0', numDigitsInName); |
4902 |
10 Jul 18 |
nicklas |
627 |
if (RESERVED_NAMES.reserve(dc, nextExternalId)) |
4902 |
10 Jul 18 |
nicklas |
628 |
{ |
4902 |
10 Jul 18 |
nicklas |
629 |
RESERVED_NAMES.releaseIfTransactionFails(dc, nextExternalId); |
4902 |
10 Jul 18 |
nicklas |
630 |
break; |
4902 |
10 Jul 18 |
nicklas |
631 |
} |
4902 |
10 Jul 18 |
nicklas |
632 |
nextIndex++; |
4902 |
10 Jul 18 |
nicklas |
633 |
numIterations++; |
4902 |
10 Jul 18 |
nicklas |
634 |
if (numIterations == 100) |
4902 |
10 Jul 18 |
nicklas |
635 |
{ |
4902 |
10 Jul 18 |
nicklas |
636 |
throw new RuntimeException("Failed to generate an external ID for '" + prefix + |
4902 |
10 Jul 18 |
nicklas |
637 |
"' after 100 tries. Last tested value was '" + nextExternalId + "'."); |
4902 |
10 Jul 18 |
nicklas |
638 |
} |
4902 |
10 Jul 18 |
nicklas |
639 |
} |
4902 |
10 Jul 18 |
nicklas |
640 |
|
4902 |
10 Jul 18 |
nicklas |
641 |
return nextExternalId; |
4902 |
10 Jul 18 |
nicklas |
642 |
} |
4902 |
10 Jul 18 |
nicklas |
643 |
|
4902 |
10 Jul 18 |
nicklas |
644 |
|
4902 |
10 Jul 18 |
nicklas |
645 |
/** |
5777 |
06 Dec 19 |
nicklas |
Generate the next auto-generated external ID based on one that was previously |
5777 |
06 Dec 19 |
nicklas |
generated and reserved. The previous ID is one that is generated by either this |
5777 |
06 Dec 19 |
nicklas |
method or {@link ##getNextExternalId(DbControl, ItemQuery, String)}. |
5777 |
06 Dec 19 |
nicklas |
@since 4.25 |
5777 |
06 Dec 19 |
nicklas |
650 |
*/ |
5777 |
06 Dec 19 |
nicklas |
651 |
public static String getNextExternalId(DbControl dc, String previousId) |
5777 |
06 Dec 19 |
nicklas |
652 |
{ |
5777 |
06 Dec 19 |
nicklas |
653 |
Pattern p = Pattern.compile("(\\D+)(\\d+)"); |
5777 |
06 Dec 19 |
nicklas |
654 |
|
5777 |
06 Dec 19 |
nicklas |
655 |
Matcher m = p.matcher(previousId); |
5777 |
06 Dec 19 |
nicklas |
656 |
if (!m.matches()) throw new InvalidDataException("Previous ID doens't match pattern \\w+\\d+: " + previousId); |
5777 |
06 Dec 19 |
nicklas |
657 |
|
5777 |
06 Dec 19 |
nicklas |
658 |
String prefix = m.group(1); |
5777 |
06 Dec 19 |
nicklas |
659 |
String baseNumber = m.group(2); |
5777 |
06 Dec 19 |
nicklas |
660 |
|
5777 |
06 Dec 19 |
nicklas |
661 |
int nextIndex = Values.getInt(baseNumber) + 1; |
5777 |
06 Dec 19 |
nicklas |
662 |
|
5777 |
06 Dec 19 |
nicklas |
663 |
int numDigitsInName = baseNumber.length(); |
5777 |
06 Dec 19 |
nicklas |
664 |
int numIterations = 0; |
5777 |
06 Dec 19 |
nicklas |
// This will hold the generated names |
5777 |
06 Dec 19 |
nicklas |
666 |
String nextExternalId; |
5777 |
06 Dec 19 |
nicklas |
667 |
while (true) |
5777 |
06 Dec 19 |
nicklas |
668 |
{ |
5777 |
06 Dec 19 |
nicklas |
669 |
nextExternalId = prefix + MD5.leftPad(Integer.toString(nextIndex), '0', numDigitsInName); |
5777 |
06 Dec 19 |
nicklas |
670 |
if (RESERVED_NAMES.reserve(dc, nextExternalId)) |
5777 |
06 Dec 19 |
nicklas |
671 |
{ |
5777 |
06 Dec 19 |
nicklas |
672 |
RESERVED_NAMES.releaseIfTransactionFails(dc, nextExternalId); |
5777 |
06 Dec 19 |
nicklas |
673 |
break; |
5777 |
06 Dec 19 |
nicklas |
674 |
} |
5777 |
06 Dec 19 |
nicklas |
675 |
nextIndex++; |
5777 |
06 Dec 19 |
nicklas |
676 |
numIterations++; |
5777 |
06 Dec 19 |
nicklas |
677 |
if (numIterations == 100) |
5777 |
06 Dec 19 |
nicklas |
678 |
{ |
5777 |
06 Dec 19 |
nicklas |
679 |
throw new RuntimeException("Failed to generate an external ID for '" + prefix + |
5777 |
06 Dec 19 |
nicklas |
680 |
"' after 100 tries. Last tested value was '" + nextExternalId + "'."); |
5777 |
06 Dec 19 |
nicklas |
681 |
} |
5777 |
06 Dec 19 |
nicklas |
682 |
} |
5777 |
06 Dec 19 |
nicklas |
683 |
|
5777 |
06 Dec 19 |
nicklas |
684 |
return nextExternalId; |
5777 |
06 Dec 19 |
nicklas |
685 |
} |
5777 |
06 Dec 19 |
nicklas |
686 |
|
5777 |
06 Dec 19 |
nicklas |
687 |
|
5777 |
06 Dec 19 |
nicklas |
688 |
/** |
4902 |
10 Jul 18 |
nicklas |
@since 4.19 |
4897 |
10 Jul 18 |
nicklas |
@see #ensureNonExistingItem(DbControl, Item, String, String) |
4897 |
10 Jul 18 |
nicklas |
691 |
*/ |
4897 |
10 Jul 18 |
nicklas |
692 |
public static String ensureNonExistingItem(DbControl dc, Subtype subtype, String name) |
4897 |
10 Jul 18 |
nicklas |
693 |
{ |
4897 |
10 Jul 18 |
nicklas |
694 |
return ensureNonExistingItem(dc, subtype.getMainType(), subtype.getName(), name); |
4897 |
10 Jul 18 |
nicklas |
695 |
} |
4897 |
10 Jul 18 |
nicklas |
696 |
|
4897 |
10 Jul 18 |
nicklas |
697 |
/** |
4897 |
10 Jul 18 |
nicklas |
Check if an item with the given name already exists or not in the database. |
4897 |
10 Jul 18 |
nicklas |
If the item exists, an exception is thrown. This method will simply check if |
4897 |
10 Jul 18 |
nicklas |
an item of the given 'itemType' and 'name' exists. No other filter is used. |
4897 |
10 Jul 18 |
nicklas |
The 'subtype' parameter is only used to give an error message if an item |
4897 |
10 Jul 18 |
nicklas |
is found. |
4897 |
10 Jul 18 |
nicklas |
@return The 'name' |
4897 |
10 Jul 18 |
nicklas |
@since 4.19 |
4897 |
10 Jul 18 |
nicklas |
705 |
*/ |
4897 |
10 Jul 18 |
nicklas |
706 |
public static String ensureNonExistingItem(DbControl dc, Item itemType, String subtype, String name) |
4897 |
10 Jul 18 |
nicklas |
707 |
{ |
4897 |
10 Jul 18 |
nicklas |
708 |
ItemQuery<?> query = (ItemQuery<?>)itemType.getQuery(); |
4897 |
10 Jul 18 |
nicklas |
709 |
query.include(Include.ALL); |
4897 |
10 Jul 18 |
nicklas |
710 |
query.restrict(Restrictions.eq(Hql.property("name"), Expressions.string(name))); |
4897 |
10 Jul 18 |
nicklas |
711 |
query.setMaxResults(1); |
4897 |
10 Jul 18 |
nicklas |
712 |
|
4897 |
10 Jul 18 |
nicklas |
713 |
List<? extends BasicItem> items = query.list(dc); |
4897 |
10 Jul 18 |
nicklas |
714 |
if (items.size() > 0) |
4897 |
10 Jul 18 |
nicklas |
715 |
{ |
4897 |
10 Jul 18 |
nicklas |
// The item already exists in the database |
4897 |
10 Jul 18 |
nicklas |
717 |
BasicItem item = items.get(0); |
4897 |
10 Jul 18 |
nicklas |
718 |
String msg = subtype + " '" + name + "' has already been registered"; |
4897 |
10 Jul 18 |
nicklas |
719 |
if (item instanceof Ownable) |
4897 |
10 Jul 18 |
nicklas |
720 |
{ |
4897 |
10 Jul 18 |
nicklas |
721 |
User owner = ((Ownable)item).getOwner(); |
4897 |
10 Jul 18 |
nicklas |
722 |
msg += " by " + owner.getName(); |
4897 |
10 Jul 18 |
nicklas |
723 |
} |
5423 |
13 May 19 |
nicklas |
724 |
if (item instanceof Registered) |
5423 |
13 May 19 |
nicklas |
725 |
{ |
5423 |
13 May 19 |
nicklas |
726 |
Date registered = getCreationDateFromChangeHistory(dc, item); |
5423 |
13 May 19 |
nicklas |
727 |
if (registered != null) |
5423 |
13 May 19 |
nicklas |
728 |
{ |
5423 |
13 May 19 |
nicklas |
729 |
long sinceMinutes = (System.currentTimeMillis() - registered.getTime()) / 3600; |
5423 |
13 May 19 |
nicklas |
730 |
if (sinceMinutes <= 1) |
5423 |
13 May 19 |
nicklas |
731 |
{ |
5423 |
13 May 19 |
nicklas |
732 |
msg += " 1 minute ago."; |
5423 |
13 May 19 |
nicklas |
733 |
} |
5423 |
13 May 19 |
nicklas |
734 |
else if (sinceMinutes <= 10) |
5423 |
13 May 19 |
nicklas |
735 |
{ |
5423 |
13 May 19 |
nicklas |
736 |
msg += " " + sinceMinutes + " minutes ago."; |
5423 |
13 May 19 |
nicklas |
737 |
} |
5423 |
13 May 19 |
nicklas |
738 |
else if (sinceMinutes <= 600) |
5423 |
13 May 19 |
nicklas |
739 |
{ |
5423 |
13 May 19 |
nicklas |
740 |
msg += " at " + Reggie.CONVERTER_TIME_TO_STRING_WITH_SEPARATOR.convert(registered) + " today."; |
5423 |
13 May 19 |
nicklas |
741 |
} |
5423 |
13 May 19 |
nicklas |
742 |
else |
5423 |
13 May 19 |
nicklas |
743 |
{ |
5423 |
13 May 19 |
nicklas |
744 |
msg += " " + Reggie.CONVERTER_DATE_TO_STRING_WITH_SEPARATOR.convert(registered) + "."; |
5423 |
13 May 19 |
nicklas |
745 |
} |
5423 |
13 May 19 |
nicklas |
746 |
} |
5423 |
13 May 19 |
nicklas |
747 |
} |
4897 |
10 Jul 18 |
nicklas |
748 |
throw new RuntimeException(msg); |
4897 |
10 Jul 18 |
nicklas |
749 |
} |
4897 |
10 Jul 18 |
nicklas |
750 |
|
4897 |
10 Jul 18 |
nicklas |
// Check reserved item |
4897 |
10 Jul 18 |
nicklas |
752 |
if (!RESERVED_NAMES.reserve(dc, name)) |
4897 |
10 Jul 18 |
nicklas |
753 |
{ |
4897 |
10 Jul 18 |
nicklas |
754 |
throw new RuntimeException(subtype + " '" + name + "' is already reserved by another process"); |
4897 |
10 Jul 18 |
nicklas |
755 |
} |
4897 |
10 Jul 18 |
nicklas |
756 |
RESERVED_NAMES.releaseAtEndOfTransaction(dc, name); |
4897 |
10 Jul 18 |
nicklas |
757 |
return name; |
4897 |
10 Jul 18 |
nicklas |
758 |
} |
4897 |
10 Jul 18 |
nicklas |
759 |
|
5423 |
13 May 19 |
nicklas |
760 |
public static Date getCreationDateFromChangeHistory(DbControl dc, BasicItem item) |
5423 |
13 May 19 |
nicklas |
761 |
{ |
5423 |
13 May 19 |
nicklas |
762 |
ItemQuery<ChangeHistory> query = ChangeHistory.getHistoryOf(item); |
5423 |
13 May 19 |
nicklas |
763 |
query.restrict(Restrictions.eq(Hql.property("changeType"), Expressions.integer(ChangeType.CREATE.getValue()))); |
5423 |
13 May 19 |
nicklas |
764 |
query.order(Orders.asc(Hql.property("hst", "time"))); |
5423 |
13 May 19 |
nicklas |
765 |
query.setMaxResults(1); |
5423 |
13 May 19 |
nicklas |
766 |
List<ChangeHistory> list = query.list(dc); |
5423 |
13 May 19 |
nicklas |
767 |
return list.size() == 0 ? null : list.get(0).getTime(); |
5423 |
13 May 19 |
nicklas |
768 |
} |
4897 |
10 Jul 18 |
nicklas |
769 |
|
4897 |
10 Jul 18 |
nicklas |
770 |
/** |
3764 |
22 Feb 16 |
nicklas |
Sorts a list of items by their index order value. Items with |
3764 |
22 Feb 16 |
nicklas |
a lower value are sorted before items with a higher value. |
3764 |
22 Feb 16 |
nicklas |
773 |
|
3764 |
22 Feb 16 |
nicklas |
@see ReggieItem#setIndexOrder(int) |
3764 |
22 Feb 16 |
nicklas |
@since 4.2 |
3764 |
22 Feb 16 |
nicklas |
776 |
*/ |
3764 |
22 Feb 16 |
nicklas |
777 |
public static class IndexOrderComparator |
3764 |
22 Feb 16 |
nicklas |
778 |
implements Comparator<ReggieItem<?>> |
3764 |
22 Feb 16 |
nicklas |
779 |
{ |
3764 |
22 Feb 16 |
nicklas |
780 |
|
3764 |
22 Feb 16 |
nicklas |
781 |
public IndexOrderComparator() |
3764 |
22 Feb 16 |
nicklas |
782 |
{} |
3764 |
22 Feb 16 |
nicklas |
783 |
|
3764 |
22 Feb 16 |
nicklas |
784 |
@Override |
3764 |
22 Feb 16 |
nicklas |
785 |
public int compare(ReggieItem<?> item1, ReggieItem<?> item2) |
3764 |
22 Feb 16 |
nicklas |
786 |
{ |
3764 |
22 Feb 16 |
nicklas |
787 |
return item1.indexOrder - item2.indexOrder; |
3764 |
22 Feb 16 |
nicklas |
788 |
} |
3764 |
22 Feb 16 |
nicklas |
789 |
} |
4985 |
28 Sep 18 |
nicklas |
790 |
|
4985 |
28 Sep 18 |
nicklas |
791 |
/** |
4985 |
28 Sep 18 |
nicklas |
Sorts a list of items by name. |
4985 |
28 Sep 18 |
nicklas |
@since 4.20 |
4985 |
28 Sep 18 |
nicklas |
794 |
*/ |
4985 |
28 Sep 18 |
nicklas |
795 |
public static class NameComparator |
4985 |
28 Sep 18 |
nicklas |
796 |
implements Comparator<ReggieItem<?>> |
4985 |
28 Sep 18 |
nicklas |
797 |
{ |
4985 |
28 Sep 18 |
nicklas |
798 |
public NameComparator() |
4985 |
28 Sep 18 |
nicklas |
799 |
{} |
4985 |
28 Sep 18 |
nicklas |
800 |
|
4985 |
28 Sep 18 |
nicklas |
801 |
@Override |
4985 |
28 Sep 18 |
nicklas |
802 |
public int compare(ReggieItem<?> item1, ReggieItem<?> item2) |
4985 |
28 Sep 18 |
nicklas |
803 |
{ |
4985 |
28 Sep 18 |
nicklas |
804 |
return item1.getName().compareTo(item2.getName()); |
4985 |
28 Sep 18 |
nicklas |
805 |
} |
4985 |
28 Sep 18 |
nicklas |
806 |
|
4985 |
28 Sep 18 |
nicklas |
807 |
} |
4985 |
28 Sep 18 |
nicklas |
808 |
|
1326 |
29 Mar 11 |
nicklas |
809 |
} |