1 package eu
.etaxonomy
.cdm
.api
.service
;
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
;
11 import java
.util
.Optional
;
13 import java
.util
.UUID
;
14 import java
.util
.stream
.Collectors
;
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
;
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
;
55 @Transactional(readOnly
= false)
56 public class DescriptiveDataSetService
57 extends IdentifiableServiceBase
<DescriptiveDataSet
, IDescriptiveDataSetDao
>
58 implements IDescriptiveDataSetService
{
60 private static Logger logger
= Logger
.getLogger(DescriptiveDataSetService
.class);
63 private IOccurrenceService occurrenceService
;
66 private ITaxonService taxonService
;
69 private IDescriptionService descriptionService
;
72 private ITaxonNodeService taxonNodeService
;
75 private IProgressMonitorService progressMonitorService
;
79 protected void setDao(IDescriptiveDataSetDao dao
) {
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
);
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
);
96 public List
<UuidAndTitleCache
<DescriptiveDataSet
>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements
, String pattern
) {
97 return dao
.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements
, pattern
);
103 public UUID
monitGetRowWrapper(DescriptiveDataSet descriptiveDataSet
) {
104 RemotingProgressMonitorThread monitorThread
= new RemotingProgressMonitorThread() {
106 public Serializable
doRun(IRemotingProgressMonitor monitor
) {
107 return getRowWrapper(descriptiveDataSet
, monitor
);
110 UUID uuid
= progressMonitorService
.registerNewRemotingMonitor(monitorThread
);
111 monitorThread
.setPriority(3);
112 monitorThread
.start();
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
<>();
125 RowWrapperDTO rowWrapper
= null;
126 if(HibernateProxyHelper
.isInstanceOf(description
, TaxonDescription
.class)){
127 rowWrapper
= createTaxonRowWrapper(HibernateProxyHelper
.deproxy(description
, TaxonDescription
.class), descriptiveDataSet
);
129 else if (HibernateProxyHelper
.isInstanceOf(description
, SpecimenDescription
.class)){
130 rowWrapper
= createSpecimenRowWrapper(HibernateProxyHelper
.deproxy(description
, SpecimenDescription
.class), descriptiveDataSet
, false);
132 if(rowWrapper
!=null){
133 wrappers
.add(rowWrapper
);
141 public Collection
<SpecimenNodeWrapper
> loadSpecimens(DescriptiveDataSet descriptiveDataSet
){
142 List
<UUID
> filteredNodes
= findFilteredTaxonNodes(descriptiveDataSet
);
143 return occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes
, null, null);
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);
153 return taxonNodeService
.uuidList(filter
);
157 public List
<TaxonNode
> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
, List
<String
> propertyPaths
){
158 return taxonNodeService
.load(findFilteredTaxonNodes(descriptiveDataSet
), propertyPaths
);
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())){
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
);
184 return new TaxonRowWrapperDTO(description
, taxonNode
);
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
197 Set
<TaxonNode
> taxonSubtreeFilter
= descriptiveDataSet
.getTaxonSubtreeFilter();
198 for (TaxonNode node
: taxonSubtreeFilter
) {
200 node
= taxonNodeService
.load(node
.getId(), Arrays
.asList("taxon"));
201 taxonNode
= findTaxonNodeForDescription(node
, specimen
);
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
);
219 //taxon node was found
222 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(specimen
.getUuid(),
223 Arrays
.asList(new String
[]{
225 "gatheringEvent.country"
227 if(fieldUnits
.size()!=1){
228 logger
.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
232 fieldUnit
= fieldUnits
.iterator().next();
235 if(HibernateProxyHelper
.isInstanceOf(specimen
, DerivedUnit
.class)){
236 identifier
= occurrenceService
.getMostSignificantIdentifier(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class));
239 if(fieldUnit
!=null && fieldUnit
.getGatheringEvent()!=null){
240 country
= fieldUnit
.getGatheringEvent().getCountry();
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
);
251 @Transactional(readOnly
= false)
252 public void updateTitleCache(Class
<?
extends DescriptiveDataSet
> clazz
, Integer stepSize
,
253 IIdentifiableEntityCacheStrategy
<DescriptiveDataSet
> cacheStrategy
, IProgressMonitor monitor
) {
255 clazz
= DescriptiveDataSet
.class;
257 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
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);
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
));
288 else if(wsFeature
.isSupportsQuantitativeData()){
289 newTaxonDescription
.addElement(QuantitativeData
.NewInstance(wsFeature
));
292 return newTaxonDescription
;
296 public SpecimenDescription
findSpecimenDescription(UUID descriptiveDataSetUuid
, UUID specimenUuid
){
297 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
298 SpecimenOrObservationBase specimen
= occurrenceService
.load(specimenUuid
);
300 Set
<Feature
> datasetFeatures
= dataSet
.getDescriptiveSystem().getDistinctFeatures();
301 List
<DescriptionElementBase
> matchingDescriptionElements
= new ArrayList
<>();
303 for (SpecimenDescription specimenDescription
: (Set
<SpecimenDescription
>) specimen
.getDescriptions()) {
304 specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescription
.getUuid());
306 //check if description is already added to data set
307 if(dataSet
.getDescriptions().contains(specimenDescription
)){
308 return specimenDescription
;
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
);
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$
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());
330 list
= new ArrayList
<>();
333 featureToElementMap
.put(element
.getFeature(), list
);
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));
342 //duplicates found -> check if all are equal
344 DescriptionElementBase match
= null;
345 for (DescriptionElementBase descriptionElementBase
: elements
) {
347 match
= descriptionElementBase
;
349 else if(!new DescriptionElementCompareWrapper(match
).equals(new DescriptionElementCompareWrapper(descriptionElementBase
))){
351 //TODO: propagate message
352 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
353 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
358 descriptionElementsToClone
.add(match
);
362 //clone matching descriptionElements
363 for (DescriptionElementBase descriptionElementBase
: descriptionElementsToClone
) {
364 DescriptionElementBase clone
;
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()));
372 else if(descriptionElementBase
instanceof QuantitativeData
){
373 TextData label
= new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData
) descriptionElementBase
, null);
374 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
377 } catch (CloneNotSupportedException e
) {
378 // MessagingUtils.error(CharacterMatrix.class, e);
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
)){
392 if(wsFeature
.isSupportsCategoricalData()){
393 newDesription
.addElement(CategoricalData
.NewInstance(wsFeature
));
395 else if(wsFeature
.isSupportsQuantitativeData()){
396 newDesription
.addElement(QuantitativeData
.NewInstance(wsFeature
));
400 return newDesription
;
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
{
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
<>();
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()));
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());
432 else if(value
.getType().equals(StatisticalMeasure
.EXACT_VALUE())){
433 exacts
.add(value
.getValue());
436 else if(value
.getType().equals(StatisticalMeasure
.MAX())){
437 maxs
.add(value
.getValue());
439 else if(value
.getType().equals(StatisticalMeasure
.MIN())){
440 mins
.add(value
.getValue());
442 else if(value
.getType().equals(StatisticalMeasure
.SAMPLE_SIZE())){
443 sampleSizes
.add(value
.getValue());
446 else if(value
.getType().equals(StatisticalMeasure
.STANDARD_DEVIATION())){
447 standardDevs
.add(value
.getValue());
449 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_LOWER_BOUNDARY())){
450 lowerBounds
.add(value
.getValue());
453 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_UPPER_BOUNDARY())){
454 upperBounds
.add(value
.getValue());
456 else if(value
.getType().equals(StatisticalMeasure
.VARIANCE())){
457 variances
.add(value
.getValue());
464 public int hashCode() {
465 final int prime
= 31;
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());
483 public boolean equals(Object obj
) {
490 if (getClass() != obj
.getClass()) {
493 DescriptionElementCompareWrapper other
= (DescriptionElementCompareWrapper
) obj
;
494 if (!getOuterType().equals(other
.getOuterType())) {
498 if (other
.avgs
!= null) {
501 } else if (!avgs
.equals(other
.avgs
)) {
504 if (element
== null) {
505 if (other
.element
!= null) {
508 } else if (!element
.equals(other
.element
)) {
511 if (exacts
== null) {
512 if (other
.exacts
!= null) {
515 } else if (!exacts
.equals(other
.exacts
)) {
518 if (lowerBounds
== null) {
519 if (other
.lowerBounds
!= null) {
522 } else if (!lowerBounds
.equals(other
.lowerBounds
)) {
526 if (other
.maxs
!= null) {
529 } else if (!maxs
.equals(other
.maxs
)) {
533 if (other
.mins
!= null) {
536 } else if (!mins
.equals(other
.mins
)) {
539 if (sampleSizes
== null) {
540 if (other
.sampleSizes
!= null) {
543 } else if (!sampleSizes
.equals(other
.sampleSizes
)) {
546 if (standardDevs
== null) {
547 if (other
.standardDevs
!= null) {
550 } else if (!standardDevs
.equals(other
.standardDevs
)) {
553 if (stateUuids
== null) {
554 if (other
.stateUuids
!= null) {
557 } else if (!stateUuids
.equals(other
.stateUuids
)) {
560 if (upperBounds
== null) {
561 if (other
.upperBounds
!= null) {
564 } else if (!upperBounds
.equals(other
.upperBounds
)) {
567 if (variances
== null) {
568 if (other
.variances
!= null) {
571 } else if (!variances
.equals(other
.variances
)) {
577 private DescriptiveDataSetService
getOuterType() {
578 return DescriptiveDataSetService
.this;