Revision 4d8fec30
Added by Andreas Müller almost 3 years ago
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/DescriptionAggregationBase.java | ||
---|---|---|
94 | 94 |
protected DeleteResult doInvoke() { |
95 | 95 |
|
96 | 96 |
try { |
97 |
//TODO FIXME use UpdateResult |
|
98 |
|
|
99 | 97 |
double start = System.currentTimeMillis(); |
100 | 98 |
IProgressMonitor monitor = getConfig().getMonitor(); |
101 | 99 |
|
... | ... | |
153 | 151 |
logger.info("Time elapsed for invoking task(): " + (end - start) / (1000) + "s"); |
154 | 152 |
|
155 | 153 |
done(); |
156 |
return getResult(); |
|
157 | 154 |
} catch (Exception e) { |
158 | 155 |
getResult().addException(new RuntimeException("Unhandled error during doInvoke", e)); |
159 |
return getResult(); |
|
160 | 156 |
} |
157 |
return getResult(); |
|
161 | 158 |
} |
162 | 159 |
|
163 | 160 |
private DeleteResult handleException(Exception e, String unhandledMessage) { |
... | ... | |
286 | 283 |
} |
287 | 284 |
|
288 | 285 |
//persist |
289 |
mergeAggregationResultIntoTargetDescription(targetDescription, resultHolder); |
|
290 |
removeDescriptionIfEmpty(targetDescription, resultHolder); //AM: necessary? Seems to be done in addAggregationResultToDescription() already... |
|
286 |
boolean updated = mergeAggregationResultIntoTargetDescription(targetDescription, resultHolder); |
|
287 |
if (updated){ |
|
288 |
getResult().addUpdatedObject(targetDescription); |
|
289 |
} |
|
290 |
removeDescriptionIfEmpty(targetDescription, resultHolder); |
|
291 | 291 |
deleteDescriptionsToDelete(resultHolder); |
292 | 292 |
} |
293 | 293 |
|
... | ... | |
300 | 300 |
getSession().flush(); // move to service method #9801 |
301 | 301 |
DeleteResult descriptionDeleteResult = repository.getDescriptionService().deleteDescription(descriptionToDelete); |
302 | 302 |
//TODO handle result somehow if not OK, but careful, descriptions may be linked >1x and therefore maybe deleted only after last link was removed |
303 |
this.getResult().includeResult(descriptionDeleteResult, true); |
|
303 |
if (descriptionDeleteResult.getDeletedObjects().contains(descriptionToDelete) && descriptionToDelete.isPersited()){ |
|
304 |
this.getResult().addDeletedObject(descriptionToDelete); |
|
305 |
} |
|
306 |
// this.getResult().includeResult(descriptionDeleteResult, true); |
|
304 | 307 |
} |
305 | 308 |
} |
306 | 309 |
} |
... | ... | |
320 | 323 |
* Removes description elements not needed anymore from their description and |
321 | 324 |
* updates the {@link DeleteResult}. |
322 | 325 |
*/ |
323 |
protected void handleDescriptionElementsToRemove(TaxonDescription targetDescription,
|
|
326 |
protected boolean handleDescriptionElementsToRemove(TaxonDescription targetDescription,
|
|
324 | 327 |
Set<? extends DescriptionElementBase> elementsToRemove) { |
328 |
boolean updated = false; |
|
325 | 329 |
//remove all elements not needed anymore |
326 | 330 |
for(DescriptionElementBase elementToRemove : elementsToRemove){ |
327 | 331 |
targetDescription.removeElement(elementToRemove); |
328 | 332 |
//AM: do we really want to add each element to the deleteResult? |
329 |
this.getResult().addDeletedObject(elementToRemove); |
|
333 |
//this.getResult().addDeletedObject(elementToRemove); |
|
334 |
updated |= elementToRemove.isPersited(); |
|
330 | 335 |
} |
336 |
return updated; |
|
331 | 337 |
} |
332 | 338 |
|
333 | 339 |
/** |
334 | 340 |
* Adds the temporary aggregated data (resultHolder) to the description. |
335 | 341 |
* Tries to reuse existing data if possible. |
336 | 342 |
*/ |
337 |
protected abstract void mergeAggregationResultIntoTargetDescription(TaxonDescription targetDescription,
|
|
343 |
protected abstract boolean mergeAggregationResultIntoTargetDescription(TaxonDescription targetDescription,
|
|
338 | 344 |
ResultHolder resultHolder); |
339 | 345 |
|
340 | 346 |
protected abstract void aggregateToParentTaxon(TaxonNode taxonNode, ResultHolder resultHolder, |
... | ... | |
369 | 375 |
|
370 | 376 |
// create a new one |
371 | 377 |
TaxonDescription newDescription = createNewDescription(taxon); |
372 |
|
|
373 |
//TODO maybe not necessary here as the new description only will be kept if not empty |
|
374 |
//(otherwise they could end up in updated and deleted objects which is unwanted) |
|
375 |
getResult().addUpdatedObject(newDescription); |
|
376 | 378 |
return newDescription; |
377 | 379 |
} |
378 | 380 |
|
... | ... | |
392 | 394 |
for(DescriptionElementBase descriptionElement : deleteCandidates) { |
393 | 395 |
aggregationDescription.removeElement(descriptionElement); |
394 | 396 |
getDescriptionService().deleteDescriptionElement(descriptionElement); |
395 |
getResult().addDeletedObject(descriptionElement); |
|
397 |
if (descriptionElement.isPersited()){ |
|
398 |
getResult().addDeletedObject(descriptionElement); |
|
399 |
} |
|
396 | 400 |
} |
397 | 401 |
getDescriptionService().saveOrUpdate(aggregationDescription); |
398 | 402 |
} |
399 | 403 |
} |
400 | 404 |
|
401 |
protected <S extends DescriptionElementBase, TE extends DefinedTermBase<?>> void mergeDescriptionElements(
|
|
405 |
protected <S extends DescriptionElementBase, TE extends DefinedTermBase<?>> boolean mergeDescriptionElements(
|
|
402 | 406 |
TaxonDescription targetDescription, Map<TE, S> newElementsMap, Class<S> debClass) { |
403 | 407 |
|
408 |
boolean updated = false; |
|
409 |
|
|
404 | 410 |
//init elements to remove |
405 | 411 |
Set<DescriptionElementBase> elementsToRemove = new HashSet<>( |
406 | 412 |
targetDescription.getElements().stream() |
... | ... | |
424 | 430 |
//if there is no element for this character in old data, add the new element for this character to the target description (otherwise reuse old element) |
425 | 431 |
if (elementToStay == null){ |
426 | 432 |
targetDescription.addElement(newElement); |
433 |
updated = true; |
|
427 | 434 |
}else{ |
428 | 435 |
elementsToRemove.remove(elementToStay); |
429 |
mergeDescriptionElement(elementToStay, newElement); |
|
436 |
updated |= mergeDescriptionElement(elementToStay, newElement);
|
|
430 | 437 |
} |
431 | 438 |
} |
432 | 439 |
|
433 |
handleDescriptionElementsToRemove(targetDescription, elementsToRemove); |
|
440 |
updated |= handleDescriptionElementsToRemove(targetDescription, elementsToRemove); |
|
441 |
return updated; |
|
434 | 442 |
} |
435 | 443 |
|
436 | 444 |
/** |
437 | 445 |
* Merges a new (temporary description element into an existing one) |
438 | 446 |
*/ |
439 | 447 |
protected abstract <S extends DescriptionElementBase> |
440 |
void mergeDescriptionElement(S targetElement, S newElement); |
|
448 |
boolean mergeDescriptionElement(S targetElement, S newElement); |
|
449 |
|
|
450 |
protected boolean mergeSourcesForDescriptionElements(DescriptionElementBase deb, |
|
451 |
Set<DescriptionElementSource> newSources) { |
|
441 | 452 |
|
442 |
protected void mergeSourcesForDescriptionElements(DescriptionElementBase deb, Set<DescriptionElementSource> newSources) {
|
|
453 |
boolean updated = false;
|
|
443 | 454 |
Set<DescriptionElementSource> toDeleteSources = new HashSet<>(deb.getSources()); |
444 | 455 |
for(DescriptionElementSource newSource : newSources) { |
445 | 456 |
boolean contained = false; |
... | ... | |
453 | 464 |
if(!contained) { |
454 | 465 |
try { |
455 | 466 |
deb.addSource(newSource.clone()); |
467 |
updated = true; |
|
456 | 468 |
} catch (CloneNotSupportedException e) { |
457 | 469 |
// should never happen |
458 | 470 |
throw new RuntimeException(e); |
... | ... | |
461 | 473 |
} |
462 | 474 |
for (DescriptionElementSource toDeleteSource : toDeleteSources){ |
463 | 475 |
deb.removeSource(toDeleteSource); |
476 |
updated |= toDeleteSource.isPersited(); |
|
464 | 477 |
} |
478 |
return updated; |
|
465 | 479 |
} |
466 | 480 |
|
467 | 481 |
protected abstract TaxonDescription createNewDescription(Taxon taxon); |
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/DistributionAggregation.java | ||
---|---|---|
24 | 24 |
import org.hibernate.search.Search; |
25 | 25 |
import org.springframework.transaction.TransactionStatus; |
26 | 26 |
|
27 |
import eu.etaxonomy.cdm.common.CdmUtils; |
|
27 | 28 |
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor; |
28 | 29 |
import eu.etaxonomy.cdm.model.common.CdmBase; |
29 | 30 |
import eu.etaxonomy.cdm.model.description.DescriptionElementBase; |
... | ... | |
233 | 234 |
} |
234 | 235 |
|
235 | 236 |
@Override |
236 |
protected void mergeAggregationResultIntoTargetDescription(TaxonDescription targetDescription,
|
|
237 |
protected boolean mergeAggregationResultIntoTargetDescription(TaxonDescription targetDescription,
|
|
237 | 238 |
ResultHolder resultHolder) { |
238 | 239 |
|
240 |
boolean updated = false; |
|
241 |
|
|
239 | 242 |
Class<? extends DescriptionElementBase> debClass = Distribution.class; |
240 | 243 |
Map<NamedArea, Distribution> accumulatedStatusMap = ((DistributionResultHolder)resultHolder).accumulatedStatusMap; |
241 | 244 |
|
... | ... | |
256 | 259 |
// create a new distribution element |
257 | 260 |
targetDistribution = newElement; |
258 | 261 |
targetDescription.addElement(targetDistribution); |
262 |
updated = true; |
|
259 | 263 |
}else{ |
260 |
mergeDescriptionElement(targetDistribution, newElement); |
|
264 |
updated |= mergeDescriptionElement(targetDistribution, newElement);
|
|
261 | 265 |
elementsToRemove.remove(targetDistribution); //we keep the distribution for reuse |
262 | 266 |
} |
263 |
mergeSourcesForDescriptionElements(targetDistribution, accumulatedStatusMap.get(area).getSources()); |
|
267 |
updated |= mergeSourcesForDescriptionElements(targetDistribution, accumulatedStatusMap.get(area).getSources());
|
|
264 | 268 |
} |
265 | 269 |
|
266 |
this.handleDescriptionElementsToRemove(targetDescription, elementsToRemove); |
|
270 |
updated |= this.handleDescriptionElementsToRemove(targetDescription, elementsToRemove); |
|
271 |
return updated; |
|
267 | 272 |
} |
268 | 273 |
|
269 | 274 |
@Override |
270 |
protected <S extends DescriptionElementBase> void mergeDescriptionElement(S targetElement,
|
|
275 |
protected <S extends DescriptionElementBase> boolean mergeDescriptionElement(S targetElement,
|
|
271 | 276 |
S newElement) { |
277 |
boolean updated = false; |
|
272 | 278 |
if (!(targetElement instanceof Distribution)){ |
273 | 279 |
throw new AggregationException("Unexpected class: " + targetElement.getClass().getName()); |
274 | 280 |
}else{ |
275 | 281 |
Distribution targetDistribution = (Distribution)targetElement; |
276 | 282 |
Distribution newDistribution = (Distribution)newElement; |
277 |
targetDistribution.setStatus(newDistribution.getStatus()); |
|
283 |
if (!CdmUtils.nullSafeEqual(targetDistribution.getStatus(), newDistribution.getStatus())){ |
|
284 |
targetDistribution.setStatus(newDistribution.getStatus()); |
|
285 |
updated = true; |
|
286 |
} |
|
278 | 287 |
} |
288 |
return updated; |
|
279 | 289 |
} |
280 | 290 |
|
281 | 291 |
@Override |
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/StructuredDescriptionAggregation.java | ||
---|---|---|
21 | 21 |
import java.util.stream.Collectors; |
22 | 22 |
|
23 | 23 |
import eu.etaxonomy.cdm.common.BigDecimalUtil; |
24 |
import eu.etaxonomy.cdm.common.CdmUtils; |
|
24 | 25 |
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor; |
25 | 26 |
import eu.etaxonomy.cdm.model.common.CdmBase; |
26 | 27 |
import eu.etaxonomy.cdm.model.common.ICdmBase; |
... | ... | |
139 | 140 |
} |
140 | 141 |
|
141 | 142 |
@Override |
142 |
protected void mergeAggregationResultIntoTargetDescription(TaxonDescription targetDescription,
|
|
143 |
protected boolean mergeAggregationResultIntoTargetDescription(TaxonDescription targetDescription,
|
|
143 | 144 |
ResultHolder resultHolder) { |
144 | 145 |
|
145 | 146 |
StructuredDescriptionResultHolder structuredResultHolder = (StructuredDescriptionResultHolder)resultHolder; |
146 |
mergeDescriptionElements(targetDescription, structuredResultHolder.categoricalMap, CategoricalData.class); |
|
147 |
mergeDescriptionElements(targetDescription, structuredResultHolder.quantitativeMap, QuantitativeData.class); |
|
148 |
addAggregationSources(targetDescription, structuredResultHolder);
|
|
147 |
boolean updated = mergeDescriptionElements(targetDescription, structuredResultHolder.categoricalMap, CategoricalData.class);
|
|
148 |
updated |= mergeDescriptionElements(targetDescription, structuredResultHolder.quantitativeMap, QuantitativeData.class);
|
|
149 |
updated |= mergeDescriptionSources(targetDescription, structuredResultHolder);
|
|
149 | 150 |
|
150 | 151 |
if(!targetDescription.getElements().isEmpty()){ |
151 | 152 |
dataSet.addDescription(targetDescription); |
152 | 153 |
}else{ |
153 | 154 |
dataSet.removeDescription(targetDescription); |
154 | 155 |
} |
156 |
return updated; |
|
155 | 157 |
} |
156 | 158 |
|
157 | 159 |
@Override |
... | ... | |
159 | 161 |
return deb.isInstanceOf(CategoricalData.class) || deb.isInstanceOf(QuantitativeData.class); |
160 | 162 |
} |
161 | 163 |
|
162 |
private <T extends DescriptionBase<?>> void addAggregationSources(TaxonDescription targetDescription,
|
|
164 |
private <T extends DescriptionBase<?>> boolean mergeDescriptionSources(TaxonDescription targetDescription,
|
|
163 | 165 |
StructuredDescriptionResultHolder structuredResultHolder) { |
164 | 166 |
|
167 |
boolean updated = false; |
|
165 | 168 |
//Remove sources from description |
166 | 169 |
Set<IdentifiableSource> sourcesToRemove = targetDescription.getSources().stream() |
167 | 170 |
.filter(source->source.getType().equals(OriginalSourceType.Aggregation)) |
... | ... | |
172 | 175 |
IdentifiableSource mergeSourceCandidate = findSourceCandidate(targetDescription, newSource); |
173 | 176 |
if (mergeSourceCandidate == null){ |
174 | 177 |
addNewSource(targetDescription, newSource); |
178 |
updated = true; |
|
175 | 179 |
}else{ |
176 |
mergeSource(mergeSourceCandidate, newSource); |
|
180 |
updated |= mergeSource(mergeSourceCandidate, newSource);
|
|
177 | 181 |
sourcesToRemove.remove(mergeSourceCandidate); |
178 | 182 |
} |
179 | 183 |
} |
... | ... | |
181 | 185 |
//remove remaining sources-to-be-removed |
182 | 186 |
for (IdentifiableSource sourceToRemove : sourcesToRemove) { |
183 | 187 |
targetDescription.removeSource(sourceToRemove); |
188 |
updated |= sourceToRemove.isPersited(); |
|
184 | 189 |
ICdmBase target = CdmBase.deproxy(sourceToRemove.getCdmSource()); |
185 | 190 |
if (target != null){ |
186 | 191 |
sourceToRemove.setCdmSource(null); //workaround for missing orphan removal #9801 |
... | ... | |
200 | 205 |
} |
201 | 206 |
} |
202 | 207 |
} |
208 |
return updated; |
|
203 | 209 |
} |
204 | 210 |
|
205 | 211 |
private <T extends DescriptionBase<?>> void addNewSource(TaxonDescription targetDescription, |
... | ... | |
219 | 225 |
} |
220 | 226 |
|
221 | 227 |
//mergeablity has been checked before |
222 |
private <T extends DescriptionBase<?>> void mergeSource(IdentifiableSource mergeCandidate, IdentifiableSource newSource) {
|
|
228 |
private <T extends DescriptionBase<?>> boolean mergeSource(IdentifiableSource mergeCandidate, IdentifiableSource newSource) {
|
|
223 | 229 |
|
230 |
boolean updated = false; |
|
224 | 231 |
ICdmBase newTarget = newSource.getCdmSource(); |
225 | 232 |
if (newTarget != null){ |
226 | 233 |
newTarget = CdmBase.deproxy(newTarget); |
... | ... | |
229 | 236 |
T newTargetDesc = (T)newTarget; |
230 | 237 |
@SuppressWarnings("unchecked") |
231 | 238 |
T existingTargetDesc = CdmBase.deproxy((T)mergeCandidate.getCdmSource()); |
232 |
mergeSourceDescription(existingTargetDesc, newTargetDesc); |
|
239 |
updated |= mergeSourceDescription(existingTargetDesc, newTargetDesc);
|
|
233 | 240 |
((IDescribable<T>)existingTargetDesc.describedEntity()).addDescription(existingTargetDesc); |
234 | 241 |
if (!existingTargetDesc.equals(newTargetDesc)){ |
235 | 242 |
((IDescribable<T>)newTargetDesc.describedEntity()).removeDescription(newTargetDesc); |
... | ... | |
242 | 249 |
}else{ |
243 | 250 |
throw new AggregationException("Sources not linking to another CdmBase instance currently not yet supported."); |
244 | 251 |
} |
252 |
return updated; |
|
245 | 253 |
} |
246 | 254 |
|
247 |
private <T extends DescriptionBase<?>> void mergeSourceDescription(T existingSourceDescription, T newSourceDescription) {
|
|
255 |
private <T extends DescriptionBase<?>> boolean mergeSourceDescription(T existingSourceDescription, T newSourceDescription) {
|
|
248 | 256 |
|
257 |
boolean updated = false; |
|
249 | 258 |
Set<DescriptionElementBase> elementsToRemove = new HashSet<>(existingSourceDescription.getElements()); |
250 | 259 |
Set<DescriptionElementBase> newElements = new HashSet<>(newSourceDescription.getElements()); |
251 | 260 |
|
... | ... | |
256 | 265 |
&& e.getFeature().equals(newElementClone.getFeature())) |
257 | 266 |
.findFirst(); |
258 | 267 |
if (matchingElement.isPresent()){ |
259 |
mergeDescriptionElement(matchingElement.get(), newElementClone); |
|
268 |
updated |= mergeDescriptionElement(matchingElement.get(), newElementClone);
|
|
260 | 269 |
elementsToRemove.remove(matchingElement.get()); |
261 | 270 |
}else{ |
262 | 271 |
existingSourceDescription.addElement(newElementClone); |
272 |
updated = true; |
|
263 | 273 |
} |
264 | 274 |
} |
265 |
addSourceDescriptionToDescribedEntity(newSourceDescription); |
|
275 |
updated |= addSourceDescriptionToDescribedEntity(newSourceDescription);
|
|
266 | 276 |
existingSourceDescription.setTitleCache(newSourceDescription.getTitleCache(), true); |
267 | 277 |
|
268 | 278 |
for (DescriptionElementBase debToRemove : elementsToRemove){ |
269 | 279 |
existingSourceDescription.removeElement(debToRemove); |
280 |
updated |= debToRemove.isPersited(); |
|
270 | 281 |
} |
271 |
|
|
282 |
return updated; |
|
272 | 283 |
} |
273 | 284 |
|
274 | 285 |
@SuppressWarnings("unchecked") |
275 |
private <T extends DescriptionBase<?>> void addSourceDescriptionToDescribedEntity(T sourceDescription) { |
|
276 |
((IDescribable<T>)sourceDescription.describedEntity()).addDescription(sourceDescription); |
|
277 |
} |
|
278 |
@SuppressWarnings("unchecked") |
|
279 |
private <T extends DescriptionBase<?>> void removeSourceDescriptionFromDescribedEntity(T sourceDescription) { |
|
280 |
((IDescribable<T>)sourceDescription.describedEntity()).removeDescription(sourceDescription); |
|
286 |
private <T extends DescriptionBase<?>> boolean addSourceDescriptionToDescribedEntity(T sourceDescription) { |
|
287 |
boolean updated = false; |
|
288 |
IDescribable<T> describedEntity = ((IDescribable<T>)sourceDescription.describedEntity()); |
|
289 |
if (describedEntity.getDescriptions().contains(sourceDescription)){ |
|
290 |
describedEntity.addDescription(sourceDescription); |
|
291 |
updated = true; |
|
292 |
} |
|
293 |
return updated; |
|
281 | 294 |
} |
282 | 295 |
|
283 | 296 |
private IdentifiableSource findSourceCandidate(TaxonDescription targetDescription, IdentifiableSource newSource) { |
... | ... | |
336 | 349 |
} |
337 | 350 |
|
338 | 351 |
@Override |
339 |
protected <S extends DescriptionElementBase> void mergeDescriptionElement(S targetElement,
|
|
352 |
protected <S extends DescriptionElementBase> boolean mergeDescriptionElement(S targetElement,
|
|
340 | 353 |
S newElement) { |
341 | 354 |
|
355 |
boolean updated = false; |
|
342 | 356 |
targetElement = CdmBase.deproxy(targetElement); |
343 | 357 |
newElement = CdmBase.deproxy(newElement); |
344 | 358 |
if (targetElement instanceof CategoricalData){ |
345 |
mergeDescriptionElement((CategoricalData)targetElement, (CategoricalData)newElement); |
|
359 |
updated |= mergeDescriptionElement((CategoricalData)targetElement, (CategoricalData)newElement);
|
|
346 | 360 |
}else if (targetElement.isInstanceOf(QuantitativeData.class)){ |
347 |
mergeDescriptionElement((QuantitativeData)targetElement, (QuantitativeData)newElement); |
|
361 |
updated |= mergeDescriptionElement((QuantitativeData)targetElement, (QuantitativeData)newElement);
|
|
348 | 362 |
}else{ |
349 | 363 |
throw new AggregationException("Class not supported: " + targetElement.getClass().getName()); |
350 | 364 |
} |
365 |
return updated; |
|
351 | 366 |
} |
352 | 367 |
|
353 |
private void mergeDescriptionElement(CategoricalData elementToStay,
|
|
368 |
private boolean mergeDescriptionElement(CategoricalData elementToStay,
|
|
354 | 369 |
CategoricalData newElement) { |
355 | 370 |
|
356 |
List<StateData> oldData = new ArrayList<>(elementToStay.getStateData()); |
|
371 |
boolean updated = false; |
|
372 |
List<StateData> dataToRemove = new ArrayList<>(elementToStay.getStateData()); |
|
357 | 373 |
List<StateData> newData = new ArrayList<>(newElement.getStateData()); |
358 | 374 |
for (StateData newStateData : newData){ |
359 | 375 |
State state = newStateData.getState(); |
360 |
StateData oldStateData = firstByState(state, oldData);
|
|
376 |
StateData oldStateData = firstByState(state, dataToRemove);
|
|
361 | 377 |
if (oldStateData != null){ |
362 | 378 |
//for now only state and count is used for aggregation, below code needs to be adapted if this changes |
363 |
oldStateData.setCount(newStateData.getCount()); |
|
364 |
oldData.remove(oldStateData); |
|
379 |
if (!CdmUtils.nullSafeEqual(oldStateData.getCount(), newStateData.getCount())){ |
|
380 |
oldStateData.setCount(newStateData.getCount()); |
|
381 |
// getResult().addUpdatedObject(oldStateData); |
|
382 |
updated = true; |
|
383 |
} |
|
384 |
dataToRemove.remove(oldStateData); |
|
365 | 385 |
}else{ |
366 | 386 |
elementToStay.addStateData(newStateData); |
387 |
updated = true; |
|
367 | 388 |
} |
368 | 389 |
} |
369 |
for (StateData stateDataToRemove : oldData){
|
|
390 |
for (StateData stateDataToRemove : dataToRemove){
|
|
370 | 391 |
elementToStay.removeStateData(stateDataToRemove); |
392 |
updated |= stateDataToRemove.isPersited(); |
|
371 | 393 |
} |
394 |
return updated; |
|
372 | 395 |
} |
373 | 396 |
|
374 | 397 |
private StateData firstByState(State state, List<StateData> oldData) { |
... | ... | |
383 | 406 |
return null; |
384 | 407 |
} |
385 | 408 |
|
386 |
private void mergeDescriptionElement(QuantitativeData elementToStay,
|
|
409 |
private boolean mergeDescriptionElement(QuantitativeData elementToStay,
|
|
387 | 410 |
QuantitativeData newElement) { |
388 | 411 |
|
412 |
boolean updated = false; |
|
413 |
|
|
389 | 414 |
Set<StatisticalMeasurementValue> oldValues = new HashSet<>(elementToStay.getStatisticalValues()); |
390 | 415 |
Set<StatisticalMeasurementValue> newValues = new HashSet<>(newElement.getStatisticalValues()); |
391 | 416 |
for (StatisticalMeasurementValue newValue : newValues){ |
... | ... | |
393 | 418 |
StatisticalMeasurementValue oldValue = firstValueByType(type, oldValues); |
394 | 419 |
if (oldValue != null){ |
395 | 420 |
//for now only state and count is used for aggregation, below code needs to be adapted if this changes |
396 |
oldValue.setValue(newValue.getValue()); |
|
421 |
if (!CdmUtils.nullSafeEqual(oldValue.getValue(), newValue.getValue())){ |
|
422 |
oldValue.setValue(newValue.getValue()); |
|
423 |
updated = true; |
|
424 |
} |
|
397 | 425 |
oldValues.remove(oldValue); |
398 | 426 |
}else{ |
399 | 427 |
elementToStay.addStatisticalValue(newValue); |
428 |
updated = true; |
|
400 | 429 |
} |
401 | 430 |
} |
402 | 431 |
for (StatisticalMeasurementValue valueToRemove : oldValues){ |
403 | 432 |
elementToStay.removeStatisticalValue(valueToRemove); |
433 |
updated |= valueToRemove.isPersited(); |
|
404 | 434 |
} |
435 |
return updated; |
|
405 | 436 |
} |
406 | 437 |
|
407 | 438 |
private StatisticalMeasurementValue firstValueByType(StatisticalMeasure type, Set<StatisticalMeasurementValue> oldValues) { |
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/description/StructuredDescriptionAggregationTest.java | ||
---|---|---|
173 | 173 |
// 1st aggregation |
174 | 174 |
DeleteResult result = engine.invoke(config, repository); |
175 | 175 |
verifyStatusOk(result); |
176 |
verifyUpdatedObjects(result, 4); |
|
176 | 177 |
commitAndStartNewTransaction(); |
177 | 178 |
verifyAggregatedDescription(new TestConfig(config)); |
178 | 179 |
|
180 |
//update data |
|
179 | 181 |
addSomeDataToFirstAggregation(); |
180 | 182 |
commitAndStartNewTransaction(); |
181 | 183 |
verifyAggregatedDescription(new TestConfig(config).setWithAddedData()); |
... | ... | |
183 | 185 |
// 2nd aggregation => should be same as originally as data was only added to aggregation to see if it is correctly deleted |
184 | 186 |
result = engine.invoke(config, repository); |
185 | 187 |
verifyStatusOk(result); |
188 |
verifyUpdatedObjects(result, 3); |
|
186 | 189 |
commitAndStartNewTransaction(); |
187 | 190 |
verifyAggregatedDescription(new TestConfig(config)); |
188 | 191 |
|
189 |
// reaggregate with an element having a feature not yet in the existing descriptions |
|
192 |
// re-aggregate with an element having a feature not yet in the existing descriptions
|
|
190 | 193 |
addNewFeature(); |
191 | 194 |
commitAndStartNewTransaction(); |
192 | 195 |
result = engine.invoke(config, repository); |
193 | 196 |
verifyStatusOk(result); |
197 |
verifyUpdatedObjects(result, 3); |
|
194 | 198 |
commitAndStartNewTransaction(); |
195 | 199 |
verifyAggregatedDescription(new TestConfig(config).setWithFeature()); |
196 |
|
|
197 | 200 |
} |
198 | 201 |
|
199 | 202 |
private void addSomeDataToFirstAggregation() { |
... | ... | |
223 | 226 |
@DataSet(value="StructuredDescriptionAggregationTest.xml"), |
224 | 227 |
}) |
225 | 228 |
public void testDeleteTest() { |
229 |
|
|
230 |
//create test data |
|
226 | 231 |
createDefaultFeatureTree(); |
227 | 232 |
DescriptiveDataSet dataSet = createTestData(); |
228 | 233 |
commitAndStartNewTransaction(); |
229 | 234 |
|
230 |
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet); |
|
231 |
|
|
232 | 235 |
// 1st aggregation |
236 |
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet); |
|
233 | 237 |
DeleteResult result = engine.invoke(config, repository); |
238 |
|
|
239 |
//verify |
|
234 | 240 |
verifyStatusOk(result); |
235 | 241 |
commitAndStartNewTransaction(); |
236 | 242 |
verifyAggregatedDescription(new TestConfig(config)); |
237 | 243 |
|
244 |
//remove data |
|
238 | 245 |
removeSomeDataFromFirstAggregation(); |
239 | 246 |
commitAndStartNewTransaction(); |
240 | 247 |
Assert.assertEquals("Should have 3 specimen desc, 1 literature desc, 2 individual association holder, " |
... | ... | |
244 | 251 |
// 2nd aggregation |
245 | 252 |
result = engine.invoke(config, repository); |
246 | 253 |
verifyStatusOk(result); |
254 |
verifyUpdatedObjects(result, 3); |
|
247 | 255 |
commitAndStartNewTransaction(); |
248 | 256 |
verifyAggregatedDescription(new TestConfig(config).setWithRemoved()); |
249 | 257 |
} |
... | ... | |
265 | 273 |
@DataSet(value="StructuredDescriptionAggregationTest.xml"), |
266 | 274 |
}) |
267 | 275 |
public void testIncompleteQuantitativeData() { |
276 |
|
|
277 |
//create test data |
|
268 | 278 |
createDefaultFeatureTree(); |
269 | 279 |
DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance(); |
270 | 280 |
datasetService.save(dataSet); |
... | ... | |
284 | 294 |
dataSet.setDescriptiveSystem(descriptiveSystem); |
285 | 295 |
commitAndStartNewTransaction(); |
286 | 296 |
|
297 |
//aggregate |
|
287 | 298 |
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet); |
288 |
|
|
289 | 299 |
DeleteResult result = engine.invoke(config, repository); |
290 |
verifyStatusOk(result); |
|
291 | 300 |
|
301 |
//verify |
|
302 |
verifyStatusOk(result); |
|
303 |
verifyUpdatedObjects(result, 3); |
|
292 | 304 |
Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID); |
293 | 305 |
TaxonDescription aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, 1); |
294 | 306 |
verifyQuantitativeData(uuidFeatureLeafLength, null, new BigDecimal("0.0"), new BigDecimal("7.0"), null, aggrDescLapsanaCommunisAlpina); |
... | ... | |
328 | 340 |
verifyStatusOk(result); |
329 | 341 |
Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID); |
330 | 342 |
//if no state at all exists in description even the description is not created (this was different before but changed with 9b8a8049439 / #9804) |
343 |
verifyUpdatedObjects(result, 0); |
|
331 | 344 |
verifyNumberTaxonDescriptions(taxLapsanaCommunisAlpina, 0); |
332 | 345 |
|
333 | 346 |
//add data for another feature |
... | ... | |
336 | 349 |
commitAndStartNewTransaction(); |
337 | 350 |
result = engine.invoke(config, repository); |
338 | 351 |
verifyStatusOk(result); |
352 |
verifyUpdatedObjects(result, 3); |
|
339 | 353 |
taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID); |
340 | 354 |
TaxonDescription aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, 1); |
341 | 355 |
verifyNumberDescriptionElements(aggrDescLapsanaCommunisAlpina, uuidFeatureLeafColor, 0); //we don't expect empty categorical data anymore #9804 |
... | ... | |
348 | 362 |
result = engine.invoke(config, repository); |
349 | 363 |
|
350 | 364 |
verifyStatusOk(result); |
365 |
verifyUpdatedObjects(result, 3); |
|
351 | 366 |
taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID); |
352 | 367 |
aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, 2); //for leafPA and for color |
353 | 368 |
List<StateData> sdAlpinaLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false); |
... | ... | |
373 | 388 |
// 1st aggregation |
374 | 389 |
DeleteResult result = engine.invoke(config, repository); |
375 | 390 |
verifyStatusOk(result); |
391 |
verifyUpdatedObjects(result, 4); |
|
376 | 392 |
commitAndStartNewTransaction(); |
377 | 393 |
verifyAggregatedDescription(new TestConfig(config)); |
378 | 394 |
|
... | ... | |
380 | 396 |
config.setToParentSourceMode(AggregationSourceMode.NONE); |
381 | 397 |
result = engine.invoke(config, repository); |
382 | 398 |
verifyStatusOk(result); |
399 |
verifyUpdatedObjects(result, 2); //"Only the 2 subspecies aggregations should be updated" |
|
383 | 400 |
commitAndStartNewTransaction(); |
384 | 401 |
verifyAggregatedDescription(new TestConfig(config)); |
385 | 402 |
|
... | ... | |
387 | 404 |
config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION); |
388 | 405 |
result = engine.invoke(config, repository); |
389 | 406 |
verifyStatusOk(result); |
407 |
verifyUpdatedObjects(result, 4); |
|
390 | 408 |
commitAndStartNewTransaction(); |
391 | 409 |
verifyAggregatedDescription(new TestConfig(config)); |
392 | 410 |
|
... | ... | |
394 | 412 |
config.setToParentSourceMode(AggregationSourceMode.TAXON); |
395 | 413 |
result = engine.invoke(config, repository); |
396 | 414 |
verifyStatusOk(result); |
415 |
verifyUpdatedObjects(result, 4); |
|
397 | 416 |
commitAndStartNewTransaction(); |
398 | 417 |
verifyAggregatedDescription(new TestConfig(config)); |
399 | 418 |
|
... | ... | |
401 | 420 |
config.setToParentSourceMode(AggregationSourceMode.TAXON); |
402 | 421 |
result = engine.invoke(config, repository); |
403 | 422 |
verifyStatusOk(result); |
423 |
verifyUpdatedObjects(result, 2); |
|
404 | 424 |
commitAndStartNewTransaction(); |
405 | 425 |
verifyAggregatedDescription(new TestConfig(config)); |
406 | 426 |
|
... | ... | |
442 | 462 |
@DataSet(value="StructuredDescriptionAggregationTest.xml"), |
443 | 463 |
}) |
444 | 464 |
public void testAggregation() { |
465 |
|
|
466 |
//create test data |
|
445 | 467 |
createDefaultFeatureTree(); |
446 | 468 |
DescriptiveDataSet dataSet = createTestData(); |
447 | 469 |
commitAndStartNewTransaction(); |
448 | 470 |
|
471 |
//aggregate |
|
449 | 472 |
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet); |
450 |
|
|
451 | 473 |
DeleteResult result = engine.invoke(config, repository); |
452 | 474 |
commitAndStartNewTransaction(); |
453 | 475 |
verifyStatusOk(result); |
454 | 476 |
verifyAggregatedDescription(new TestConfig(config)); |
455 | 477 |
|
478 |
//aggregate again with literature (also tests re-aggregation issues) |
|
456 | 479 |
config.setIncludeLiterature(true); |
457 |
|
|
458 | 480 |
result = engine.invoke(config, repository); |
459 | 481 |
commitAndStartNewTransaction(); |
460 | 482 |
verifyStatusOk(result); |
... | ... | |
470 | 492 |
} |
471 | 493 |
} |
472 | 494 |
|
495 |
private void verifyUpdatedObjects(DeleteResult result, int n) { |
|
496 |
Assert.assertEquals(n, result.getUpdatedObjects().size()); |
|
497 |
} |
|
498 |
|
|
473 | 499 |
private void verifyStatusError(DeleteResult result, String expectedMessage) { |
474 | 500 |
if (result.getStatus() != DeleteResult.Status.ERROR){ |
475 | 501 |
Assert.fail("Aggregation should fail with status error " + result.toString()); |
Also available in: Unified diff
fix #9858 add updatedObjects to aggregation result for aggregated descriptions