Merge branch 'develop' into taxonDecription
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / DescriptiveDataSetService.java
1 package eu.etaxonomy.cdm.api.service;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.Collection;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Optional;
12 import java.util.Set;
13 import java.util.UUID;
14 import java.util.stream.Collectors;
15
16 import org.apache.log4j.Logger;
17 import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.stereotype.Service;
19 import org.springframework.transaction.annotation.Transactional;
20
21 import eu.etaxonomy.cdm.api.service.dto.RowWrapperDTO;
22 import eu.etaxonomy.cdm.api.service.dto.SpecimenRowWrapperDTO;
23 import eu.etaxonomy.cdm.api.service.dto.TaxonRowWrapperDTO;
24 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
25 import eu.etaxonomy.cdm.common.monitor.IRemotingProgressMonitor;
26 import eu.etaxonomy.cdm.common.monitor.RemotingProgressMonitorThread;
27 import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
28 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
29 import eu.etaxonomy.cdm.model.common.Language;
30 import eu.etaxonomy.cdm.model.common.MarkerType;
31 import eu.etaxonomy.cdm.model.description.CategoricalData;
32 import eu.etaxonomy.cdm.model.description.DescriptionBase;
33 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
34 import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
35 import eu.etaxonomy.cdm.model.description.DescriptiveSystemRole;
36 import eu.etaxonomy.cdm.model.description.Feature;
37 import eu.etaxonomy.cdm.model.description.QuantitativeData;
38 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
39 import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
40 import eu.etaxonomy.cdm.model.description.TaxonDescription;
41 import eu.etaxonomy.cdm.model.description.TextData;
42 import eu.etaxonomy.cdm.model.location.NamedArea;
43 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
44 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
45 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
46 import eu.etaxonomy.cdm.model.taxon.Classification;
47 import eu.etaxonomy.cdm.model.taxon.Taxon;
48 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
49 import eu.etaxonomy.cdm.persistence.dao.description.IDescriptiveDataSetDao;
50 import eu.etaxonomy.cdm.persistence.dto.SpecimenNodeWrapper;
51 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
52 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
53
54 @Service
55 @Transactional(readOnly = false)
56 public class DescriptiveDataSetService
57 extends IdentifiableServiceBase<DescriptiveDataSet, IDescriptiveDataSetDao>
58 implements IDescriptiveDataSetService {
59
60 private static Logger logger = Logger.getLogger(DescriptiveDataSetService.class);
61
62 @Autowired
63 private IOccurrenceService occurrenceService;
64
65 @Autowired
66 private ITaxonService taxonService;
67
68 @Autowired
69 private IDescriptionService descriptionService;
70
71 @Autowired
72 private ITaxonNodeService taxonNodeService;
73
74 @Autowired
75 private IProgressMonitorService progressMonitorService;
76
77 @Override
78 @Autowired
79 protected void setDao(IDescriptiveDataSetDao dao) {
80 this.dao = dao;
81 }
82
83 @Override
84 public Map<DescriptionBase, Set<DescriptionElementBase>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet, Set<Feature> features, Integer pageSize, Integer pageNumber,
85 List<String> propertyPaths) {
86 return dao.getDescriptionElements(descriptiveDataSet, features, pageSize, pageNumber, propertyPaths);
87 }
88
89 @Override
90 public <T extends DescriptionElementBase> Map<UuidAndTitleCache, Map<UUID, Set<T>>> getTaxonFeatureDescriptionElementMap(
91 Class<T> clazz, UUID descriptiveDataSetUuid, DescriptiveSystemRole role) {
92 return dao.getTaxonFeatureDescriptionElementMap(clazz, descriptiveDataSetUuid, role);
93 }
94
95 @Override
96 public List<UuidAndTitleCache<DescriptiveDataSet>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements, String pattern) {
97 return dao.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements, pattern);
98 }
99
100
101 @Override
102 @Transactional
103 public UUID monitGetRowWrapper(DescriptiveDataSet descriptiveDataSet) {
104 RemotingProgressMonitorThread monitorThread = new RemotingProgressMonitorThread() {
105 @Override
106 public Serializable doRun(IRemotingProgressMonitor monitor) {
107 return getRowWrapper(descriptiveDataSet, monitor);
108 }
109 };
110 UUID uuid = progressMonitorService.registerNewRemotingMonitor(monitorThread);
111 monitorThread.setPriority(3);
112 monitorThread.start();
113 return uuid;
114 }
115
116 @Override
117 public ArrayList<RowWrapperDTO> getRowWrapper(DescriptiveDataSet descriptiveDataSet, IProgressMonitor monitor) {
118 monitor.beginTask("Load row wrapper", descriptiveDataSet.getDescriptions().size());
119 ArrayList<RowWrapperDTO> wrappers = new ArrayList<>();
120 Set<DescriptionBase> descriptions = descriptiveDataSet.getDescriptions();
121 for (DescriptionBase description : descriptions) {
122 if(monitor.isCanceled()){
123 return new ArrayList<>();
124 }
125 RowWrapperDTO rowWrapper = null;
126 if(HibernateProxyHelper.isInstanceOf(description, TaxonDescription.class)){
127 rowWrapper = createTaxonRowWrapper(HibernateProxyHelper.deproxy(description, TaxonDescription.class), descriptiveDataSet);
128 }
129 else if (HibernateProxyHelper.isInstanceOf(description, SpecimenDescription.class)){
130 rowWrapper = createSpecimenRowWrapper(HibernateProxyHelper.deproxy(description, SpecimenDescription.class), descriptiveDataSet, false);
131 }
132 if(rowWrapper!=null){
133 wrappers.add(rowWrapper);
134 }
135 monitor.worked(1);
136 }
137 return wrappers;
138 }
139
140 @Override
141 public Collection<SpecimenNodeWrapper> loadSpecimens(DescriptiveDataSet descriptiveDataSet){
142 List<UUID> filteredNodes = findFilteredTaxonNodes(descriptiveDataSet);
143 return occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes, null, null);
144 }
145
146 @Override
147 public List<UUID> findFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet){
148 TaxonNodeFilter filter = TaxonNodeFilter.NewRankInstance(descriptiveDataSet.getMinRank(), descriptiveDataSet.getMaxRank());
149 descriptiveDataSet.getGeoFilter().forEach(area -> filter.orArea(area.getUuid()));
150 descriptiveDataSet.getTaxonSubtreeFilter().forEach(node -> filter.orSubtree(node));
151 filter.setIncludeUnpublished(true);
152
153 return taxonNodeService.uuidList(filter);
154 }
155
156 @Override
157 public List<TaxonNode> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet, List<String> propertyPaths){
158 return taxonNodeService.load(findFilteredTaxonNodes(descriptiveDataSet), propertyPaths);
159 }
160
161 private TaxonNode findTaxonNodeForDescription(TaxonNode taxonNode, SpecimenOrObservationBase specimen){
162 Collection<SpecimenNodeWrapper> nodeWrapper = occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(Arrays.asList(taxonNode.getUuid()), null, null);
163 for (SpecimenNodeWrapper specimenNodeWrapper : nodeWrapper) {
164 if(specimenNodeWrapper.getUuidAndTitleCache().getId().equals(specimen.getId())){
165 return taxonNode;
166 }
167 }
168 return null;
169 }
170
171 @Override
172 public TaxonRowWrapperDTO createTaxonRowWrapper(TaxonDescription description,
173 DescriptiveDataSet descriptiveDataSet) {
174 TaxonNode taxonNode = null;
175 Classification classification = null;
176 Optional<TaxonNode> first = descriptiveDataSet.getTaxonSubtreeFilter().stream()
177 .filter(node->node.getClassification()!=null).findFirst();
178 Optional<Classification> classificationOptional = first.map(node->node.getClassification());
179 if(classificationOptional.isPresent()){
180 classification = classificationOptional.get();
181 Taxon taxon = (Taxon) taxonService.load(description.getTaxon().getId(), Arrays.asList("taxonNodes", "taxonNodes.classification"));
182 taxonNode = taxon.getTaxonNode(classification);
183 }
184 return new TaxonRowWrapperDTO(description, taxonNode);
185 }
186
187 @Override
188 public SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, DescriptiveDataSet descriptiveDataSet,
189 boolean createDefaultTaxonDescription){
190 SpecimenOrObservationBase specimen = description.getDescribedSpecimenOrObservation();
191 TaxonNode taxonNode = null;
192 FieldUnit fieldUnit = null;
193 String identifier = null;
194 NamedArea country = null;
195 //supplemental information
196 //get taxon node
197 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
198 for (TaxonNode node : taxonSubtreeFilter) {
199 //check for node
200 node = taxonNodeService.load(node.getId(), Arrays.asList("taxon"));
201 taxonNode = findTaxonNodeForDescription(node, specimen);
202 if(taxonNode!=null){
203 break;
204 }
205 else{
206 //check for child nodes
207 List<TaxonNode> allChildren = taxonNodeService.loadChildNodesOfTaxonNode(node, Arrays.asList("taxon"), true, true, null);
208 for (TaxonNode child : allChildren) {
209 taxonNode = findTaxonNodeForDescription(child, specimen);
210 if(taxonNode!=null){
211 break;
212 }
213 }
214 }
215 }
216 if(taxonNode==null){
217 return null;
218 }
219 //taxon node was found
220
221 //get field unit
222 Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(specimen.getUuid(),
223 Arrays.asList(new String[]{
224 "gatheringEvent",
225 "gatheringEvent.country"
226 }));
227 if(fieldUnits.size()!=1){
228 logger.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
229 return null;
230 }
231 else{
232 fieldUnit = fieldUnits.iterator().next();
233 }
234 //get identifier
235 if(HibernateProxyHelper.isInstanceOf(specimen, DerivedUnit.class)){
236 identifier = occurrenceService.getMostSignificantIdentifier(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
237 }
238 //get country
239 if(fieldUnit!=null && fieldUnit.getGatheringEvent()!=null){
240 country = fieldUnit.getGatheringEvent().getCountry();
241 }
242 //get default taxon description
243 TaxonDescription defaultTaxonDescription = findDefaultTaxonDescription(descriptiveDataSet.getUuid(),
244 taxonNode.getUuid(), createDefaultTaxonDescription);
245 TaxonRowWrapperDTO taxonRowWrapper = defaultTaxonDescription != null
246 ? createTaxonRowWrapper(defaultTaxonDescription, descriptiveDataSet) : null;
247 return new SpecimenRowWrapperDTO(description, taxonNode, fieldUnit, identifier, country, taxonRowWrapper);
248 }
249
250 @Override
251 @Transactional(readOnly = false)
252 public void updateTitleCache(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
253 IIdentifiableEntityCacheStrategy<DescriptiveDataSet> cacheStrategy, IProgressMonitor monitor) {
254 if (clazz == null) {
255 clazz = DescriptiveDataSet.class;
256 }
257 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
258 }
259
260 @Override
261 public TaxonDescription findDefaultTaxonDescription(UUID descriptiveDataSetUuid, UUID taxonNodeUuid, boolean create){
262 DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
263 TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid, Arrays.asList("taxon", "taxon.descriptions", "taxon.descriptions.markers"));
264 Set<DescriptionBase> dataSetDescriptions = dataSet.getDescriptions();
265 //filter out COMPUTED descriptions
266 List<TaxonDescription> nonComputedDescriptions = taxonNode.getTaxon().getDescriptions().stream()
267 .filter(desc -> desc.getMarkers().stream()
268 .noneMatch(marker -> marker.getMarkerType().equals(MarkerType.COMPUTED())))
269 .collect(Collectors.toList());
270 for (TaxonDescription taxonDescription : nonComputedDescriptions) {
271 for (DescriptionBase description : dataSetDescriptions) {
272 if(description.getUuid().equals(taxonDescription.getUuid())){
273 return HibernateProxyHelper.deproxy(descriptionService.load(taxonDescription.getUuid(),
274 Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription.class);
275 }
276 }
277 }
278 if(!create){
279 return null;
280 }
281 //description not yet added to dataset -> create a new one
282 TaxonDescription newTaxonDescription = TaxonDescription.NewInstance(taxonNode.getTaxon());
283 newTaxonDescription.setTitleCache("[Default] "+dataSet.getLabel()+": "+newTaxonDescription.generateTitle(), true); //$NON-NLS-2$
284 dataSet.getDescriptiveSystem().getDistinctFeatures().forEach(wsFeature->{
285 if(wsFeature.isSupportsCategoricalData()){
286 newTaxonDescription.addElement(CategoricalData.NewInstance(wsFeature));
287 }
288 else if(wsFeature.isSupportsQuantitativeData()){
289 newTaxonDescription.addElement(QuantitativeData.NewInstance(wsFeature));
290 }
291 });
292 return newTaxonDescription;
293 }
294
295 @Override
296 public SpecimenDescription findSpecimenDescription(UUID descriptiveDataSetUuid, UUID specimenUuid){
297 DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
298 SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
299
300 Set<Feature> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctFeatures();
301 List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
302
303 for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
304 specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescription.getUuid());
305
306 //check if description is already added to data set
307 if(dataSet.getDescriptions().contains(specimenDescription)){
308 return specimenDescription;
309 }
310
311 //gather specimen description features and check for match with dataset features
312 Set<Feature> specimenDescriptionFeatures = new HashSet<>();
313 for (DescriptionElementBase specimenDescriptionElement : specimenDescription.getElements()) {
314 Feature feature = specimenDescriptionElement.getFeature();
315 specimenDescriptionFeatures.add(feature);
316 if(datasetFeatures.contains(feature)){
317 matchingDescriptionElements.add(specimenDescriptionElement);
318 }
319 }
320 }
321 //Create new specimen description if description has not already been added to the dataset
322 SpecimenDescription newDesription = SpecimenDescription.NewInstance(specimen);
323 newDesription.setTitleCache("Dataset "+dataSet.getLabel()+": "+newDesription.generateTitle(), true); //$NON-NLS-2$
324
325 //check for equals description element (same feature and same values)
326 Map<Feature, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
327 for(DescriptionElementBase element:matchingDescriptionElements){
328 List<DescriptionElementBase> list = featureToElementMap.get(element.getFeature());
329 if(list==null){
330 list = new ArrayList<>();
331 }
332 list.add(element);
333 featureToElementMap.put(element.getFeature(), list);
334 }
335 Set<DescriptionElementBase> descriptionElementsToClone = new HashSet<>();
336 for(Feature feature:featureToElementMap.keySet()){
337 List<DescriptionElementBase> elements = featureToElementMap.get(feature);
338 //no duplicate description elements found for this feature
339 if(elements.size()==1){
340 descriptionElementsToClone.add(elements.get(0));
341 }
342 //duplicates found -> check if all are equal
343 else{
344 DescriptionElementBase match = null;
345 for (DescriptionElementBase descriptionElementBase : elements) {
346 if(match==null){
347 match = descriptionElementBase;
348 }
349 else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
350 match = null;
351 //TODO: propagate message
352 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
353 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
354 break;
355 }
356 }
357 if(match!=null){
358 descriptionElementsToClone.add(match);
359 }
360 }
361 }
362 //clone matching descriptionElements
363 for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
364 DescriptionElementBase clone;
365 try {
366 clone = descriptionElementBase.clone(newDesription);
367 clone.getSources().forEach(source -> {
368 if(descriptionElementBase instanceof CategoricalData){
369 TextData label = new DefaultCategoricalDescriptionBuilder().build((CategoricalData) descriptionElementBase, null);
370 source.setOriginalNameString(label.getText(Language.DEFAULT()));
371 }
372 else if(descriptionElementBase instanceof QuantitativeData){
373 TextData label = new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData) descriptionElementBase, null);
374 source.setOriginalNameString(label.getText(Language.DEFAULT()));
375 }
376 });
377 } catch (CloneNotSupportedException e) {
378 // MessagingUtils.error(CharacterMatrix.class, e);
379 }
380 }
381
382 //add all remaining description elements to the new description
383 for(Feature wsFeature:datasetFeatures){
384 boolean featureFound = false;
385 for(DescriptionElementBase element:newDesription.getElements()){
386 if(element.getFeature().equals(wsFeature)){
387 featureFound = true;
388 break;
389 }
390 }
391 if(!featureFound){
392 if(wsFeature.isSupportsCategoricalData()){
393 newDesription.addElement(CategoricalData.NewInstance(wsFeature));
394 }
395 else if(wsFeature.isSupportsQuantitativeData()){
396 newDesription.addElement(QuantitativeData.NewInstance(wsFeature));
397 }
398 }
399 }
400 return newDesription;
401
402 }
403
404 //TODO: this should either be solved in the model class itself
405 //OR this should cover all possibilities including modifiers for example
406 private class DescriptionElementCompareWrapper {
407
408 private DescriptionElementBase element;
409 private Set<UUID> stateUuids = new HashSet<>();
410 private Set<Float> avgs = new HashSet<>();
411 private Set<Float> exacts = new HashSet<>();
412 private Set<Float> maxs = new HashSet<>();
413 private Set<Float> mins = new HashSet<>();
414 private Set<Float> sampleSizes = new HashSet<>();
415 private Set<Float> standardDevs = new HashSet<>();
416 private Set<Float> lowerBounds = new HashSet<>();
417 private Set<Float> upperBounds = new HashSet<>();
418 private Set<Float> variances = new HashSet<>();
419
420 public DescriptionElementCompareWrapper(DescriptionElementBase element) {
421 this.element = element;
422 if(element.isInstanceOf(CategoricalData.class)){
423 CategoricalData elementData = (CategoricalData)element;
424 elementData.getStatesOnly().forEach(state->stateUuids.add(state.getUuid()));
425 }
426 else if(element.isInstanceOf(QuantitativeData.class)){
427 QuantitativeData elementData = (QuantitativeData)element;
428 elementData.getStatisticalValues().forEach(value->{
429 if(value.getType().equals(StatisticalMeasure.AVERAGE())){
430 avgs.add(value.getValue());
431 }
432 else if(value.getType().equals(StatisticalMeasure.EXACT_VALUE())){
433 exacts.add(value.getValue());
434
435 }
436 else if(value.getType().equals(StatisticalMeasure.MAX())){
437 maxs.add(value.getValue());
438 }
439 else if(value.getType().equals(StatisticalMeasure.MIN())){
440 mins.add(value.getValue());
441 }
442 else if(value.getType().equals(StatisticalMeasure.SAMPLE_SIZE())){
443 sampleSizes.add(value.getValue());
444
445 }
446 else if(value.getType().equals(StatisticalMeasure.STANDARD_DEVIATION())){
447 standardDevs.add(value.getValue());
448 }
449 else if(value.getType().equals(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY())){
450 lowerBounds.add(value.getValue());
451
452 }
453 else if(value.getType().equals(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY())){
454 upperBounds.add(value.getValue());
455 }
456 else if(value.getType().equals(StatisticalMeasure.VARIANCE())){
457 variances.add(value.getValue());
458 }
459 });
460 }
461 }
462
463 @Override
464 public int hashCode() {
465 final int prime = 31;
466 int result = 1;
467 result = prime * result + getOuterType().hashCode();
468 result = prime * result + ((avgs == null) ? 0 : avgs.hashCode());
469 result = prime * result + ((element == null) ? 0 : element.hashCode());
470 result = prime * result + ((exacts == null) ? 0 : exacts.hashCode());
471 result = prime * result + ((lowerBounds == null) ? 0 : lowerBounds.hashCode());
472 result = prime * result + ((maxs == null) ? 0 : maxs.hashCode());
473 result = prime * result + ((mins == null) ? 0 : mins.hashCode());
474 result = prime * result + ((sampleSizes == null) ? 0 : sampleSizes.hashCode());
475 result = prime * result + ((standardDevs == null) ? 0 : standardDevs.hashCode());
476 result = prime * result + ((stateUuids == null) ? 0 : stateUuids.hashCode());
477 result = prime * result + ((upperBounds == null) ? 0 : upperBounds.hashCode());
478 result = prime * result + ((variances == null) ? 0 : variances.hashCode());
479 return result;
480 }
481
482 @Override
483 public boolean equals(Object obj) {
484 if (this == obj) {
485 return true;
486 }
487 if (obj == null) {
488 return false;
489 }
490 if (getClass() != obj.getClass()) {
491 return false;
492 }
493 DescriptionElementCompareWrapper other = (DescriptionElementCompareWrapper) obj;
494 if (!getOuterType().equals(other.getOuterType())) {
495 return false;
496 }
497 if (avgs == null) {
498 if (other.avgs != null) {
499 return false;
500 }
501 } else if (!avgs.equals(other.avgs)) {
502 return false;
503 }
504 if (element == null) {
505 if (other.element != null) {
506 return false;
507 }
508 } else if (!element.equals(other.element)) {
509 return false;
510 }
511 if (exacts == null) {
512 if (other.exacts != null) {
513 return false;
514 }
515 } else if (!exacts.equals(other.exacts)) {
516 return false;
517 }
518 if (lowerBounds == null) {
519 if (other.lowerBounds != null) {
520 return false;
521 }
522 } else if (!lowerBounds.equals(other.lowerBounds)) {
523 return false;
524 }
525 if (maxs == null) {
526 if (other.maxs != null) {
527 return false;
528 }
529 } else if (!maxs.equals(other.maxs)) {
530 return false;
531 }
532 if (mins == null) {
533 if (other.mins != null) {
534 return false;
535 }
536 } else if (!mins.equals(other.mins)) {
537 return false;
538 }
539 if (sampleSizes == null) {
540 if (other.sampleSizes != null) {
541 return false;
542 }
543 } else if (!sampleSizes.equals(other.sampleSizes)) {
544 return false;
545 }
546 if (standardDevs == null) {
547 if (other.standardDevs != null) {
548 return false;
549 }
550 } else if (!standardDevs.equals(other.standardDevs)) {
551 return false;
552 }
553 if (stateUuids == null) {
554 if (other.stateUuids != null) {
555 return false;
556 }
557 } else if (!stateUuids.equals(other.stateUuids)) {
558 return false;
559 }
560 if (upperBounds == null) {
561 if (other.upperBounds != null) {
562 return false;
563 }
564 } else if (!upperBounds.equals(other.upperBounds)) {
565 return false;
566 }
567 if (variances == null) {
568 if (other.variances != null) {
569 return false;
570 }
571 } else if (!variances.equals(other.variances)) {
572 return false;
573 }
574 return true;
575 }
576
577 private DescriptiveDataSetService getOuterType() {
578 return DescriptiveDataSetService.this;
579 }
580
581 }
582
583 }