2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.cdm
.io
.common
;
12 import java
.net
.MalformedURLException
;
14 import java
.net
.URISyntaxException
;
15 import java
.sql
.ResultSet
;
16 import java
.sql
.SQLException
;
17 import java
.util
.Arrays
;
18 import java
.util
.HashSet
;
19 import java
.util
.List
;
21 import java
.util
.UUID
;
23 import org
.apache
.commons
.lang
.StringUtils
;
24 import org
.apache
.log4j
.Logger
;
26 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
27 import eu
.etaxonomy
.cdm
.common
.media
.ImageInfo
;
28 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
29 import eu
.etaxonomy
.cdm
.io
.common
.mapping
.IInputTransformer
;
30 import eu
.etaxonomy
.cdm
.io
.common
.mapping
.UndefinedTransformerMethodException
;
31 import eu
.etaxonomy
.cdm
.io
.markup
.MarkupTransformer
;
32 import eu
.etaxonomy
.cdm
.model
.common
.AnnotationType
;
33 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
34 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTermBase
;
35 import eu
.etaxonomy
.cdm
.model
.common
.DescriptionElementSource
;
36 import eu
.etaxonomy
.cdm
.model
.common
.ExtensionType
;
37 import eu
.etaxonomy
.cdm
.model
.common
.Figure
;
38 import eu
.etaxonomy
.cdm
.model
.common
.IOriginalSource
;
39 import eu
.etaxonomy
.cdm
.model
.common
.ISourceable
;
40 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
41 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
42 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
43 import eu
.etaxonomy
.cdm
.model
.common
.Marker
;
44 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
45 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
46 import eu
.etaxonomy
.cdm
.model
.common
.Representation
;
47 import eu
.etaxonomy
.cdm
.model
.common
.TermVocabulary
;
48 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
49 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
50 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
51 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
52 import eu
.etaxonomy
.cdm
.model
.description
.Modifier
;
53 import eu
.etaxonomy
.cdm
.model
.description
.PresenceTerm
;
54 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
55 import eu
.etaxonomy
.cdm
.model
.description
.State
;
56 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
57 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
58 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
59 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
60 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
61 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaLevel
;
62 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
63 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
64 import eu
.etaxonomy
.cdm
.model
.media
.ImageFile
;
65 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
66 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
67 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
68 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
69 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
70 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
71 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
72 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
73 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
74 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
75 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
76 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
83 public abstract class CdmImportBase
<CONFIG
extends IImportConfigurator
, STATE
extends ImportStateBase
> extends CdmIoBase
<STATE
> implements ICdmImport
<CONFIG
, STATE
>{
84 private static Logger logger
= Logger
.getLogger(CdmImportBase
.class);
86 protected static final boolean CREATE
= true;
87 protected static final boolean IMAGE_GALLERY
= true;
88 protected static final boolean READ_MEDIA_DATA
= true;
90 public static final UUID uuidUserDefinedNamedAreaLevelVocabulary
= UUID
.fromString("255144da-8d95-457e-a327-9752a8f85e5a");
91 public static final UUID uuidUserDefinedNamedAreaVocabulary
= UUID
.fromString("b2238399-a3af-4f6d-b7eb-ff5d0899bf1b");
92 public static final UUID uuidUserDefinedExtensionTypeVocabulary
= UUID
.fromString("e28c1394-1be8-4847-8b81-ab44eb6d5bc8");
93 public static final UUID uuidUserDefinedReferenceSystemVocabulary
= UUID
.fromString("467591a3-10b4-4bf1-9239-f06ece33e90a");
94 public static final UUID uuidUserDefinedFeatureVocabulary
= UUID
.fromString("fe5fccb3-a2f2-4b97-b199-6e2743cf1627");
95 public static final UUID uuidUserDefinedMeasurementUnitVocabulary
= UUID
.fromString("d5e72bb7-f312-4080-bb86-c695d04a6e66");
96 public static final UUID uuidUserDefinedStatisticalMeasureVocabulary
= UUID
.fromString("62a89836-c730-4b4f-a904-3d859dbfc400");
97 public static final UUID uuidUserDefinedStateVocabulary
= UUID
.fromString("f7cddb49-8392-4db1-8640-65b48a0e6d13");
98 public static final UUID uuidUserDefinedTaxonRelationshipTypeVocabulary
= UUID
.fromString("31a324dc-408d-4877-891f-098db21744c6");
99 public static final UUID uuidUserDefinedAnnotationTypeVocabulary
= UUID
.fromString("cd9ecdd2-9cae-4890-9032-ad83293ae883");
100 public static final UUID uuidUserDefinedMarkerTypeVocabulary
= UUID
.fromString("5f02a261-fd7d-4fce-bbe4-21472de8cd51");
101 public static final UUID uuidUserDefinedRankVocabulary
= UUID
.fromString("4dc57931-38e2-46c3-974d-413b087646ba");
103 public static final UUID uuidUserDefinedModifierVocabulary
= UUID
.fromString("2a8b3838-3a95-49ea-9ab2-3049614b5884");
107 private static final String UuidOnly
= "UUIDOnly";
108 private static final String UuidLabel
= "UUID or label";
109 private static final String UuidLabelAbbrev
= "UUID, label or abbreviation";
110 private static final String UuidAbbrev
= "UUID or abbreviation";
112 public enum TermMatchMode
{
113 UUID_ONLY(0, UuidOnly
)
114 ,UUID_LABEL(1, UuidLabel
)
115 ,UUID_LABEL_ABBREVLABEL(2, UuidLabelAbbrev
)
116 ,UUID_ABBREVLABEL(3, UuidAbbrev
)
121 private String representation
;
122 private TermMatchMode(int id
, String representation
){
124 this.representation
= representation
;
129 public String
getRepresentation() {
130 return representation
;
132 public TermMatchMode
valueOf(int id
){
134 case 0: return UUID_ONLY
;
135 case 1: return UUID_LABEL
;
136 case 2: return UUID_LABEL_ABBREVLABEL
;
137 case 3: return UUID_ABBREVLABEL
;
138 default: return UUID_ONLY
;
145 protected Classification
makeTree(STATE state
, Reference reference
){
146 String treeName
= "Classification (Import)";
147 if (reference
!= null && StringUtils
.isNotBlank(reference
.getTitleCache())){
148 treeName
= reference
.getTitleCache();
150 Classification tree
= Classification
.NewInstance(treeName
);
151 tree
.setReference(reference
);
154 // use defined uuid for first tree
155 CONFIG config
= (CONFIG
)state
.getConfig();
156 if (state
.countTrees() < 1 ){
157 tree
.setUuid(config
.getClassificationUuid());
159 getClassificationService().save(tree
);
160 state
.putTree(reference
, tree
);
166 * Alternative memory saving method variant of
167 * {@link #makeTree(STATE state, Reference ref)} which stores only the
168 * UUID instead of the full tree in the <code>ImportStateBase</code> by
169 * using <code>state.putTreeUuid(ref, tree);</code>
175 protected Classification
makeTreeMemSave(STATE state
, Reference ref
){
176 String treeName
= "Classification (Import)";
177 if (ref
!= null && StringUtils
.isNotBlank(ref
.getTitleCache())){
178 treeName
= ref
.getTitleCache();
180 Classification tree
= Classification
.NewInstance(treeName
);
181 tree
.setReference(ref
);
184 // use defined uuid for first tree
185 CONFIG config
= (CONFIG
)state
.getConfig();
186 if (state
.countTrees() < 1 ){
187 tree
.setUuid(config
.getClassificationUuid());
189 getClassificationService().save(tree
);
190 state
.putTreeUuid(ref
, tree
);
195 protected ExtensionType
getExtensionType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
){
196 return getExtensionType(state
, uuid
, label
, text
, labelAbbrev
, null);
198 protected ExtensionType
getExtensionType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<ExtensionType
> voc
){
200 uuid
= UUID
.randomUUID();
202 ExtensionType extensionType
= state
.getExtensionType(uuid
);
203 if (extensionType
== null){
204 extensionType
= (ExtensionType
)getTermService().find(uuid
);
205 if (extensionType
== null){
206 extensionType
= ExtensionType
.NewInstance(text
, label
, labelAbbrev
);
207 extensionType
.setUuid(uuid
);
209 boolean isOrdered
= false;
210 voc
= getVocabulary(uuidUserDefinedExtensionTypeVocabulary
, "User defined vocabulary for extension types", "User Defined Extension Types", null, null, isOrdered
, extensionType
);
212 voc
.addTerm(extensionType
);
213 getTermService().saveOrUpdate(extensionType
);
215 state
.putExtensionType(extensionType
);
217 return extensionType
;
221 protected MarkerType
getMarkerType(STATE state
, String keyString
) {
222 IInputTransformer transformer
= state
.getTransformer();
223 MarkerType markerType
= null;
225 markerType
= transformer
.getMarkerTypeByKey(keyString
);
226 } catch (UndefinedTransformerMethodException e
) {
227 logger
.info("getMarkerTypeByKey not yet implemented for this import");
229 if (markerType
== null ){
232 uuid
= transformer
.getMarkerTypeUuid(keyString
);
233 return getMarkerType(state
, uuid
, keyString
, keyString
, keyString
);
234 } catch (UndefinedTransformerMethodException e
) {
235 logger
.warn("getMarkerTypeUuid not yet implemented for this import");
241 protected MarkerType
getMarkerType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
){
242 return getMarkerType(state
, uuid
, label
, text
, labelAbbrev
, null);
246 protected MarkerType
getMarkerType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<MarkerType
> voc
){
248 uuid
= UUID
.randomUUID();
250 MarkerType markerType
= state
.getMarkerType(uuid
);
251 if (markerType
== null){
252 markerType
= (MarkerType
)getTermService().find(uuid
);
253 if (markerType
== null){
254 markerType
= MarkerType
.NewInstance(label
, text
, labelAbbrev
);
255 markerType
.setUuid(uuid
);
257 boolean isOrdered
= false;
258 voc
= getVocabulary(uuidUserDefinedMarkerTypeVocabulary
, "User defined vocabulary for marker types", "User Defined Marker Types", null, null, isOrdered
, markerType
);
260 voc
.addTerm(markerType
);
261 getTermService().save(markerType
);
263 state
.putMarkerType(markerType
);
268 protected AnnotationType
getAnnotationType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<AnnotationType
> voc
){
270 uuid
= UUID
.randomUUID();
272 AnnotationType annotationType
= state
.getAnnotationType(uuid
);
273 if (annotationType
== null){
274 annotationType
= (AnnotationType
)getTermService().find(uuid
);
275 if (annotationType
== null){
276 annotationType
= AnnotationType
.NewInstance(label
, text
, labelAbbrev
);
277 annotationType
.setUuid(uuid
);
279 boolean isOrdered
= false;
280 voc
= getVocabulary(uuidUserDefinedAnnotationTypeVocabulary
, "User defined vocabulary for annotation types", "User Defined Annotation Types", null, null, isOrdered
, annotationType
);
283 voc
.addTerm(annotationType
);
284 getTermService().save(annotationType
);
286 state
.putAnnotationType(annotationType
);
288 return annotationType
;
292 protected ReferenceSystem
getReferenceSystem(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary voc
){
294 uuid
= UUID
.randomUUID();
296 ReferenceSystem refSystem
= state
.getReferenceSystem(uuid
);
297 if (refSystem
== null){
298 refSystem
= (ReferenceSystem
)getTermService().find(uuid
);
299 if (refSystem
== null){
300 refSystem
= ReferenceSystem
.NewInstance(text
, label
, labelAbbrev
);
302 boolean isOrdered
= false;
303 voc
= getVocabulary(uuidUserDefinedReferenceSystemVocabulary
, "User defined vocabulary for reference systems", "User Defined Reference System", null, null, isOrdered
, refSystem
);
305 voc
.addTerm(refSystem
);
306 refSystem
.setUuid(uuid
);
307 getTermService().save(refSystem
);
309 state
.putReferenceSystem(refSystem
);
317 protected Rank
getRank(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
,OrderedTermVocabulary
<Rank
> voc
, Rank lowerRank
){
319 uuid
= UUID
.randomUUID();
321 Rank rank
= state
.getRank(uuid
);
323 rank
= (Rank
)getTermService().find(uuid
);
325 rank
= new Rank(text
, label
, labelAbbrev
);
327 boolean isOrdered
= true;
328 voc
= (OrderedTermVocabulary
)getVocabulary(uuidUserDefinedRankVocabulary
, "User defined vocabulary for ranks", "User Defined Reference System", null, null, isOrdered
, rank
);
330 if (lowerRank
== null){
333 voc
.addTermAbove(rank
, lowerRank
);
336 getTermService().save(rank
);
345 * Returns a named area for a given uuid by first . If the named area does not
355 protected NamedArea
getNamedArea(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, NamedAreaType areaType
, NamedAreaLevel level
){
356 return getNamedArea(state
, uuid
, label
, text
, labelAbbrev
, areaType
, level
, null, null);
359 protected NamedArea
getNamedArea(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, NamedAreaType areaType
, NamedAreaLevel level
, TermVocabulary voc
, TermMatchMode matchMode
){
360 Class
<NamedArea
> clazz
= NamedArea
.class;
362 uuid
= UUID
.randomUUID();
364 if (matchMode
== null){
365 matchMode
= TermMatchMode
.UUID_ONLY
;
367 NamedArea namedArea
= state
.getNamedArea(uuid
);
368 if (namedArea
== null){
369 DefinedTermBase
<?
> term
= getTermService().find(uuid
);
370 namedArea
= CdmBase
.deproxy(term
,NamedArea
.class);
371 //TODO matching still experimental
372 if (namedArea
== null && (matchMode
.equals(TermMatchMode
.UUID_LABEL
) || matchMode
.equals(TermMatchMode
.UUID_LABEL_ABBREVLABEL
))){
374 Pager
<NamedArea
> areaPager
= (Pager
)getTermService().findByTitle(clazz
, label
, null, null, null, null, null, null);
375 namedArea
= findBestMatchingArea(areaPager
, uuid
, label
, text
, labelAbbrev
, areaType
, level
, voc
);
377 if (namedArea
== null && (matchMode
.equals(TermMatchMode
.UUID_ABBREVLABEL
) || matchMode
.equals(TermMatchMode
.UUID_LABEL_ABBREVLABEL
))){
378 Pager
<NamedArea
> areaPager
= getTermService().findByRepresentationAbbreviation(labelAbbrev
, clazz
, null, null);
379 namedArea
= findBestMatchingArea(areaPager
, uuid
, label
, text
, labelAbbrev
, areaType
, level
, voc
);
382 if (namedArea
== null){
383 namedArea
= NamedArea
.NewInstance(text
, label
, labelAbbrev
);
385 boolean isOrdered
= true;
386 voc
= getVocabulary(uuidUserDefinedNamedAreaVocabulary
, "User defined vocabulary for named areas", "User Defined Named Areas", null, null, isOrdered
, namedArea
);
388 voc
.addTerm(namedArea
);
389 namedArea
.setType(areaType
);
390 namedArea
.setLevel(level
);
391 namedArea
.setUuid(uuid
);
392 getTermService().save(namedArea
);
394 state
.putNamedArea(namedArea
);
400 private NamedArea
findBestMatchingArea(Pager
<NamedArea
> areaPager
, UUID uuid
, String label
, String text
, String abbrev
,
401 NamedAreaType areaType
, NamedAreaLevel level
, TermVocabulary voc
) {
402 // TODO preliminary implementation
403 List
<NamedArea
> list
= areaPager
.getRecords();
404 if (list
.size() == 0){
406 }else if (list
.size() == 1){
408 }else if (list
.size() > 1){
409 String message
= "There is more than 1 matching area for %s, %s, %s. As a preliminary implementation I take the first";
410 message
= String
.format(message
, label
, abbrev
, text
);
411 logger
.warn(message
);
418 protected NamedAreaLevel
getNamedAreaLevel(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<NamedAreaLevel
> voc
){
420 uuid
= UUID
.randomUUID();
422 NamedAreaLevel namedAreaLevel
= state
.getNamedAreaLevel(uuid
);
423 if (namedAreaLevel
== null){
424 //TODO propPath just for testing
425 List
<String
> propPath
= Arrays
.asList("vocabulary");
426 DefinedTermBase
<NamedAreaLevel
> term
= getTermService().load(uuid
, propPath
);
427 namedAreaLevel
= CdmBase
.deproxy(term
, NamedAreaLevel
.class);
428 if (namedAreaLevel
== null){
429 namedAreaLevel
= NamedAreaLevel
.NewInstance(text
, label
, labelAbbrev
);
431 boolean isOrdered
= true;
432 voc
= getVocabulary(uuidUserDefinedNamedAreaLevelVocabulary
, "User defined vocabulary for named area levels", "User Defined Named Area Levels", null, null, isOrdered
, namedAreaLevel
);
434 //FIXME only for debugging
435 Set
<NamedAreaLevel
> terms
= voc
.getTerms();
436 for (NamedAreaLevel level
: terms
){
437 TermVocabulary
<NamedAreaLevel
> levelVoc
= level
.getVocabulary();
438 if (levelVoc
== null){
439 logger
.error("ONLY FOR DEBUG: Level voc is null");
441 logger
.info("ONLY FOR DEBUG: Level voc is not null");
444 voc
.addTerm(namedAreaLevel
);
445 namedAreaLevel
.setUuid(uuid
);
446 getTermService().save(namedAreaLevel
);
448 state
.putNamedAreaLevel(namedAreaLevel
);
450 return namedAreaLevel
;
454 * Returns a {@link State} if it exists. <code>null</code> otherwise.
457 * @return {@link State}
459 protected State
getStateTerm(STATE state
, UUID uuid
){
460 return getStateTerm(state
, uuid
, null, null, null, null);
465 * Returns a {@link State} for a given uuid by first checking if the uuid has already been used in this import, if not
466 * checking if the state exists in the database, if not creating it anew (with vocabulary etc.).
467 * If label, text and labelAbbrev are all <code>null</code> no state is created.
476 protected State
getStateTerm(STATE importState
, UUID uuid
, String label
, String text
, String labelAbbrev
, OrderedTermVocabulary
<State
> voc
) {
480 State stateTerm
= importState
.getStateTerm(uuid
);
481 if (stateTerm
== null){
482 stateTerm
= CdmBase
.deproxy(getTermService().find(uuid
), State
.class);
483 if (stateTerm
== null && ! hasNoLabel(label
, text
, labelAbbrev
)){
484 stateTerm
= State
.NewInstance(text
, label
, labelAbbrev
);
485 stateTerm
.setUuid(uuid
);
487 boolean isOrdered
= true;
488 TermVocabulary
<State
> orderedVoc
= getVocabulary(uuidUserDefinedStateVocabulary
, "User defined vocabulary for states used by Categorical Data", "User Defined States", null, null, isOrdered
, stateTerm
);
489 voc
= CdmBase
.deproxy(orderedVoc
, OrderedTermVocabulary
.class);
491 voc
.addTerm(stateTerm
);
492 getTermService().save(stateTerm
);
494 logger
.warn("No label provided for new state with uuid " + uuid
);
496 importState
.putStateTerm(stateTerm
);
502 * Returns a feature if it exists, null otherwise.
503 * @see #getFeature(ImportStateBase, UUID, String, String, String, TermVocabulary)
508 protected Feature
getFeature(STATE state
, UUID uuid
){
509 return getFeature(state
, uuid
, null, null, null, null);
513 * Returns a feature for a given uuid by first checking if the uuid has already been used in this import, if not
514 * checking if the feature exists in the database, if not creating it anew (with vocabulary etc.).
515 * If label, text and labelAbbrev are all <code>null</code> no feature is created.
523 protected Feature
getFeature(STATE state
, UUID uuid
, String label
, String description
, String labelAbbrev
, TermVocabulary
<Feature
> voc
){
527 Feature feature
= state
.getFeature(uuid
);
528 if (feature
== null){
529 feature
= (Feature
)getTermService().find(uuid
);
530 if (feature
== null && ! hasNoLabel(label
, description
, labelAbbrev
)){
531 feature
= Feature
.NewInstance(description
, label
, labelAbbrev
);
532 feature
.setUuid(uuid
);
533 feature
.setSupportsTextData(true);
534 // UUID uuidFeatureVoc = UUID.fromString("b187d555-f06f-4d65-9e53-da7c93f8eaa8");
536 boolean isOrdered
= false;
537 voc
= getVocabulary(uuidUserDefinedFeatureVocabulary
, "User defined vocabulary for features", "User Defined Features", null, null, isOrdered
, feature
);
539 voc
.addTerm(feature
);
540 getTermService().save(feature
);
542 state
.putFeature(feature
);
548 * Returns a {@link MeasurementUnit} for a given uuid by first checking if the uuid has already been used in this import, if not
549 * checking if the {@link MeasurementUnit} exists in the database, if not creating it anew (with vocabulary etc.).
550 * If label, text and labelAbbrev are all <code>null</code> no {@link MeasurementUnit} is created.
558 protected MeasurementUnit
getMeasurementUnit(STATE state
, UUID uuid
, String label
, String description
, String labelAbbrev
, TermVocabulary
<MeasurementUnit
> voc
){
562 MeasurementUnit unit
= state
.getMeasurementUnit(uuid
);
564 unit
= (MeasurementUnit
)getTermService().find(uuid
);
565 if (unit
== null && ! hasNoLabel(label
, description
, labelAbbrev
)){
566 unit
= MeasurementUnit
.NewInstance(description
, label
, labelAbbrev
);
569 boolean isOrdered
= false;
570 voc
= getVocabulary(uuidUserDefinedMeasurementUnitVocabulary
, "User defined vocabulary for measurement units", "User Defined Measurement Units", null, null, isOrdered
, unit
);
573 getTermService().save(unit
);
575 state
.putMeasurementUnit(unit
);
581 * Returns a {@link StatisticalMeasure} for a given uuid by first checking if the uuid has already been used in this import, if not
582 * checking if the {@link StatisticalMeasure} exists in the database, if not creating it anew (with vocabulary etc.).
583 * If label, text and labelAbbrev are all <code>null</code> no {@link StatisticalMeasure} is created.
591 protected StatisticalMeasure
getStatisticalMeasure(STATE state
, UUID uuid
, String label
, String description
, String labelAbbrev
, TermVocabulary
<StatisticalMeasure
> voc
){
595 StatisticalMeasure statisticalMeasure
= state
.getStatisticalMeasure(uuid
);
596 if (statisticalMeasure
== null){
597 statisticalMeasure
= (StatisticalMeasure
)getTermService().find(uuid
);
598 if (statisticalMeasure
== null && ! hasNoLabel(label
, description
, labelAbbrev
)){
599 statisticalMeasure
= StatisticalMeasure
.NewInstance(description
, label
, labelAbbrev
);
600 statisticalMeasure
.setUuid(uuid
);
602 boolean isOrdered
= false;
603 voc
= getVocabulary(uuidUserDefinedStatisticalMeasureVocabulary
, "User defined vocabulary for statistical measures", "User Defined Statistical Measures", null, null, isOrdered
, statisticalMeasure
);
605 voc
.addTerm(statisticalMeasure
);
606 getTermService().save(statisticalMeasure
);
608 state
.putStatisticalMeasure(statisticalMeasure
);
610 return statisticalMeasure
;
614 * Returns a {@link Modifier} for a given uuid by first checking if the uuid has already been used in this import, if not
615 * checking if the {@link Modifier} exists in the database, if not creating it anew (with vocabulary etc.).
616 * If label, text and labelAbbrev are all <code>null</code> no {@link Modifier} is created.
624 protected Modifier
getModifier(STATE state
, UUID uuid
, String label
, String description
, String labelAbbrev
, TermVocabulary
<Modifier
> voc
){
628 Modifier modifier
= state
.getModifier(uuid
);
629 if (modifier
== null){
630 modifier
= (Modifier
)getTermService().find(uuid
);
631 if (modifier
== null && ! hasNoLabel(label
, description
, labelAbbrev
)){
632 modifier
= Modifier
.NewInstance(description
, label
, labelAbbrev
);
633 modifier
.setUuid(uuid
);
635 boolean isOrdered
= false;
636 voc
= getVocabulary(uuidUserDefinedModifierVocabulary
, "User defined vocabulary for modifier", "User Defined Modifier", null, null, isOrdered
, modifier
);
638 voc
.addTerm(modifier
);
639 getTermService().save(modifier
);
641 state
.putModifier(modifier
);
647 * Returns a taxon relationship type for a given uuid by first checking if the uuid has already been used in this import, if not
648 * checking if the taxon relationship type exists in the database, if not creating it anew (with vocabulary etc.).
649 * If label, text and labelAbbrev are all <code>null</code> no taxon relationship type is created.
657 protected TaxonRelationshipType
getTaxonRelationshipType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<TaxonRelationshipType
> voc
){
661 TaxonRelationshipType relType
= state
.getTaxonRelationshipType(uuid
);
662 if (relType
== null){
663 relType
= (TaxonRelationshipType
)getTermService().find(uuid
);
664 if (relType
== null && ! hasNoLabel(label
, text
, labelAbbrev
)){
665 relType
= new TaxonRelationshipType();
666 Representation repr
= Representation
.NewInstance(text
, label
, labelAbbrev
, Language
.DEFAULT());
667 relType
.addRepresentation(repr
);
668 relType
.setUuid(uuid
);
670 boolean isOrdered
= true;
671 voc
= getVocabulary(uuidUserDefinedTaxonRelationshipTypeVocabulary
, "User defined vocabulary for taxon relationship types", "User Defined Taxon Relationship Types", null, null, isOrdered
, relType
);
673 voc
.addTerm(relType
);
674 getTermService().save(relType
);
676 state
.putTaxonRelationshipType(relType
);
681 private boolean hasNoLabel(String label
, String text
, String labelAbbrev
) {
682 return label
== null && text
== null && labelAbbrev
== null;
687 * Returns a presence term for a given uuid by first ...
695 protected PresenceTerm
getPresenceTerm(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
){
699 PresenceTerm presenceTerm
= state
.getPresenceTerm(uuid
);
700 if (presenceTerm
== null){
701 presenceTerm
= (PresenceTerm
)getTermService().find(uuid
);
702 if (presenceTerm
== null){
703 presenceTerm
= PresenceTerm
.NewInstance(text
, label
, labelAbbrev
);
704 presenceTerm
.setUuid(uuid
);
705 //set vocabulary ; FIXME use another user-defined vocabulary
706 UUID uuidPresenceVoc
= UUID
.fromString("adbbbe15-c4d3-47b7-80a8-c7d104e53a05");
707 TermVocabulary
<PresenceTerm
> voc
= getVocabularyService().find(uuidPresenceVoc
);
708 voc
.addTerm(presenceTerm
);
709 getTermService().save(presenceTerm
);
711 state
.putPresenceTerm(presenceTerm
);
717 * Returns a language for a given uuid by first ...
725 protected Language
getLanguage(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
){
726 return getLanguage(state
, uuid
, label
, text
, labelAbbrev
, null);
729 protected Language
getLanguage(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary voc
){
733 Language language
= state
.getLanguage(uuid
);
734 if (language
== null){
735 language
= (Language
)getTermService().find(uuid
);
736 if (language
== null){
737 language
= Language
.NewInstance(text
, label
, labelAbbrev
);
739 language
.setUuid(uuid
);
741 UUID uuidLanguageVoc
= UUID
.fromString("463a96f1-20ba-4a4c-9133-854c1682bd9b");
742 boolean isOrdered
= false;
743 voc
= getVocabulary(uuidLanguageVoc
, "User defined languages", "User defined languages", "User defined languages", null, isOrdered
, language
);
745 //set vocabulary ; FIXME use another user-defined vocabulary
747 voc
.addTerm(language
);
748 getTermService().save(language
);
750 state
.putLanguage(language
);
761 protected <T
extends DefinedTermBase
> TermVocabulary
<T
> getVocabulary(UUID uuid
, String text
, String label
, String abbrev
, URI termSourceUri
, boolean isOrdered
, T type
) {
762 List
<String
> propPath
= Arrays
.asList(new String
[]{"terms"});
763 TermVocabulary
<T
> voc
= getVocabularyService().load(uuid
, propPath
);
766 voc
= OrderedTermVocabulary
.NewInstance(text
, label
, abbrev
, termSourceUri
);
768 voc
= TermVocabulary
.NewInstance(text
, label
, abbrev
, termSourceUri
);
771 getVocabularyService().save(voc
);
777 * Adds an orginal source to a sourceable objects (implemented for Identifiable entity and description element.
778 * If cdmBase is not sourceable nothing happens.
779 * TODO Move to DbImportBase once this exists.
780 * TODO also implemented in DbImportObjectCreationMapper (reduce redundance)
783 * @param dbIdAttribute
786 * @throws SQLException
788 public void addOriginalSource(CdmBase cdmBase
, Object idAttributeValue
, String namespace
, Reference citation
) {
789 if (cdmBase
instanceof ISourceable
){
790 IOriginalSource source
;
791 ISourceable sourceable
= (ISourceable
)cdmBase
;
792 Object id
= idAttributeValue
;
793 String strId
= String
.valueOf(id
);
794 String microCitation
= null;
795 if (cdmBase
instanceof IdentifiableEntity
){
796 source
= IdentifiableSource
.NewInstance(strId
, namespace
, citation
, microCitation
);
797 }else if (cdmBase
instanceof DescriptionElementBase
){
798 source
= DescriptionElementSource
.NewInstance(strId
, namespace
, citation
, microCitation
);
800 logger
.warn("ISourceable not beeing identifiable entities or description element base are not yet supported. CdmBase is of type " + cdmBase
.getClass().getName() + ". Original source not added.");
803 sourceable
.addSource(source
);
804 }else if (cdmBase
!= null){
805 logger
.warn("Sourced object does not implement ISourceable: " + cdmBase
.getClass() + "," + cdmBase
.getUuid());
807 logger
.warn("Sourced object is null");
812 * @see #addOriginalSource(CdmBase, Object, String, Reference)
815 * @param dbIdAttribute
818 * @throws SQLException
820 public void addOriginalSource(ResultSet rs
, CdmBase cdmBase
, String dbIdAttribute
, String namespace
, Reference citation
) throws SQLException
{
821 Object id
= rs
.getObject(dbIdAttribute
);
822 addOriginalSource(cdmBase
, id
, namespace
, citation
);
827 * If the child taxon is missing genus or species epithet information and the rank is below <i>genus</i>
828 * or <i>species</i> respectively the according epithets are taken from the parent taxon.
829 * If the name is an autonym and has no combination author/basionym author the authors are taken from
834 protected void fillMissingEpithetsForTaxa(Taxon parentTaxon
, Taxon childTaxon
) {
835 NonViralName parentName
= HibernateProxyHelper
.deproxy(parentTaxon
.getName(), NonViralName
.class);
836 NonViralName childName
= HibernateProxyHelper
.deproxy(childTaxon
.getName(), NonViralName
.class);
837 fillMissingEpithets(parentName
, childName
);
841 * If the child name is missing genus or species epithet information and the rank is below <i>genus</i>
842 * or <i>species</i> respectively the according epithets are taken from the parent name.
843 * If the name is an autonym and has no combination author/basionym author the authors are taken from
848 protected void fillMissingEpithets(NonViralName parentName
, NonViralName childName
) {
849 if (StringUtils
.isBlank(childName
.getGenusOrUninomial()) && childName
.getRank().isLower(Rank
.GENUS()) ){
850 childName
.setGenusOrUninomial(parentName
.getGenusOrUninomial());
853 if (StringUtils
.isBlank(childName
.getSpecificEpithet()) && childName
.getRank().isLower(Rank
.SPECIES()) ){
854 childName
.setSpecificEpithet(parentName
.getSpecificEpithet());
856 if (childName
.isAutonym() && childName
.getCombinationAuthorTeam() == null && childName
.getBasionymAuthorTeam() == null ){
857 childName
.setCombinationAuthorTeam(parentName
.getCombinationAuthorTeam());
858 childName
.setBasionymAuthorTeam(parentName
.getBasionymAuthorTeam());
863 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
864 * an arbitrary one is chosen.
865 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
866 * is <code>true</code>.
867 * @param createNewIfNotExists
868 * @param isImageGallery if true only taxon description being image galleries are considered.
869 * If false only taxon description being no image galleries are considered.
872 public TaxonNameDescription
getTaxonNameDescription(TaxonNameBase name
, boolean isImageGallery
, boolean createNewIfNotExists
) {
873 Reference ref
= null;
874 return getTaxonNameDescription(name
, ref
, isImageGallery
, createNewIfNotExists
);
878 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
879 * Only matches a description if the given reference is a source of the description.<BR>
880 * If a new description is created the given reference will be added as a source.
882 * @see #getTaxonDescription(Taxon, boolean, boolean)
884 public TaxonNameDescription
getTaxonNameDescription(TaxonNameBase
<?
,?
> name
, Reference ref
, boolean isImageGallery
, boolean createNewIfNotExists
) {
885 TaxonNameDescription result
= null;
886 Set
<TaxonNameDescription
> descriptions
= name
.getDescriptions();
887 for (TaxonNameDescription description
: descriptions
){
888 if (description
.isImageGallery() == isImageGallery
){
889 if (hasCorrespondingSource(ref
, description
)){
890 result
= description
;
895 if (result
== null && createNewIfNotExists
){
896 result
= TaxonNameDescription
.NewInstance(name
);
897 result
.setImageGallery(isImageGallery
);
899 result
.addSource(null, null, ref
, null);
906 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
907 * an arbitrary one is chosen.
908 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
909 * is <code>true</code>.
910 * @param createNewIfNotExists
911 * @param isImageGallery if true only taxon description being image galleries are considered.
912 * If false only taxon description being no image galleries are considered.
915 public TaxonDescription
getTaxonDescription(Taxon taxon
, boolean isImageGallery
, boolean createNewIfNotExists
) {
916 Reference
<?
> ref
= null;
917 return getTaxonDescription(taxon
, ref
, isImageGallery
, createNewIfNotExists
);
921 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
922 * Only matches a description if the given reference is a source of the description.<BR>
923 * If a new description is created the given reference will be added as a source.
925 * @see #getTaxonDescription(Taxon, boolean, boolean)
927 public TaxonDescription
getTaxonDescription(Taxon taxon
, Reference ref
, boolean isImageGallery
, boolean createNewIfNotExists
) {
928 TaxonDescription result
= null;
929 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
930 for (TaxonDescription description
: descriptions
){
931 if (description
.isImageGallery() == isImageGallery
){
932 if (hasCorrespondingSource(ref
, description
)){
933 result
= description
;
938 if (result
== null && createNewIfNotExists
){
939 result
= TaxonDescription
.NewInstance(taxon
);
940 result
.setImageGallery(isImageGallery
);
942 result
.addSource(null, null, ref
, null);
950 * Returns the {@link SpecimenDescription specimen description} for a {@link SpecimenOrObservationBase specimen or observation}.
951 * If there are multiple specimen descriptions an arbitrary one is chosen.
952 * If no specimen description exists, a new one is created if <code>createNewIfNotExists</code> is <code>true</code>.
953 * @param createNewIfNotExists
954 * @param isImageGallery if true only specimen description being image galleries are considered.
955 * If false only specimen description being no image galleries are considered.
958 public SpecimenDescription
getSpecimenDescription(SpecimenOrObservationBase specimen
, boolean isImageGallery
, boolean createNewIfNotExists
) {
959 Reference ref
= null;
960 return getSpecimenDescription(specimen
, ref
, isImageGallery
, createNewIfNotExists
);
964 * Like {@link #getSpecimenDescription(SpecimenOrObservationBase, boolean, boolean)}
965 * Only matches a description if the given reference is a source of the description.<BR>
966 * If a new description is created the given reference will be added as a source.
968 * @see #getTaxonDescription(Taxon, boolean, boolean)
970 public SpecimenDescription
getSpecimenDescription(SpecimenOrObservationBase specimen
, Reference ref
, boolean isImageGallery
, boolean createNewIfNotExists
) {
971 SpecimenDescription result
= null;
972 Set
<SpecimenDescription
> descriptions
= specimen
.getDescriptions();
973 for (SpecimenDescription description
: descriptions
){
974 if (description
.isImageGallery() == isImageGallery
){
975 if (hasCorrespondingSource(ref
, description
)){
976 result
= description
;
981 if (result
== null && createNewIfNotExists
){
982 result
= SpecimenDescription
.NewInstance(specimen
);
983 result
.setImageGallery(isImageGallery
);
985 result
.addSource(null, null, ref
, null);
993 * Returns the textdata that holds general information about a feature for a taxon description.
994 * This is mainly necessary for descriptions that have more than one description element for
995 * a given feature such as 'distribution', 'description' or 'common name'. It may also hold
996 * for hierarchical features where no description element exists for a higher hierarchy level.
997 * Example: the description feature has subfeatures. But some information like authorship, figures,
998 * sources need to be added to the description itself.
999 * Currently a feature placeholder is marked by a marker of type 'feature placeholder'. Maybe in future
1000 * there will be a boolean marker in the TextData class itself.
1005 * @param createIfNotExists
1008 protected TextData
getFeaturePlaceholder(STATE state
, DescriptionBase
<?
> description
, Feature feature
, boolean createIfNotExists
) {
1009 UUID featurePlaceholderUuid
= MarkupTransformer
.uuidFeaturePlaceholder
;
1010 for (DescriptionElementBase element
: description
.getElements()){
1011 if (element
.isInstanceOf(TextData
.class)){
1012 TextData textData
= CdmBase
.deproxy(element
, TextData
.class);
1013 if (textData
.getFeature() == null || ! textData
.getFeature().equals(feature
)){
1016 for (Marker marker
: textData
.getMarkers()){
1017 MarkerType markerType
= marker
.getMarkerType();
1018 if (markerType
!= null &&
1019 markerType
.getUuid().equals(featurePlaceholderUuid
) &&
1020 marker
.getValue() == true){
1026 if (createIfNotExists
){
1027 TextData newPlaceholder
= TextData
.NewInstance(feature
);
1028 MarkerType placeholderMarkerType
= getMarkerType(state
, featurePlaceholderUuid
, "Feature Placeholder", "Feature Placeholder", null);
1029 Marker marker
= Marker
.NewInstance(placeholderMarkerType
, true);
1030 newPlaceholder
.addMarker(marker
);
1031 description
.addElement(newPlaceholder
);
1032 return newPlaceholder
;
1041 * Returns true, if this description has a source with a citation equal to the given reference.
1042 * Returns true if the given reference is null.
1044 * @param description
1046 private boolean hasCorrespondingSource(Reference
<?
> ref
, DescriptionBase
<?
> description
) {
1048 for (IdentifiableSource source
: description
.getSources()){
1049 if (ref
.equals(source
.getCitation())){
1061 * Returns the accepted taxon of a {@link TaxonBase taxon base}. <BR>
1062 * If taxonBase is of type taxon the same object is returned. If taxonBase is of type
1063 * synonym the accepted taxon is returned if one exists. If no accepted taxon exists
1064 * <code>null</code> is returned. If multiple accepted taxa exist the one taxon with the
1065 * same secundum reference is returned. If no such single taxon exists an
1066 * {@link IllegalStateException illegal state exception} is thrown.
1070 protected Taxon
getAcceptedTaxon(TaxonBase
<?
> taxonBase
) {
1071 if (taxonBase
== null){
1073 }else if(taxonBase
.isInstanceOf(Taxon
.class)){
1074 return CdmBase
.deproxy(taxonBase
, Taxon
.class);
1075 }else if(taxonBase
.isInstanceOf(Synonym
.class)){
1076 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1077 Set
<Taxon
> acceptedTaxa
= synonym
.getAcceptedTaxa();
1078 if (acceptedTaxa
.size() == 0){
1080 }else if (acceptedTaxa
.size() == 1){
1081 return acceptedTaxa
.iterator().next();
1083 Reference
<?
> sec
= synonym
.getSec();
1085 Set
<Taxon
> taxaWithSameSec
= new HashSet
<Taxon
>();
1086 for (Taxon taxon
: acceptedTaxa
){
1087 if (sec
.equals(taxon
.getSec())){
1088 taxaWithSameSec
.add(taxon
);
1091 if (taxaWithSameSec
.size() == 1){
1092 return taxaWithSameSec
.iterator().next();
1095 throw new IllegalStateException("Can't define the one accepted taxon for a synonym out of multiple accept taxa");
1098 throw new IllegalStateException("Unknown TaxonBase subclass: " + taxonBase
.getClass().getName());
1107 * @param readDataFromUrl
1108 * @see #READ_MEDIA_DATA
1110 * @throws MalformedURLException
1112 protected Media
getImageMedia(String uriString
, boolean readMediaData
, boolean isFigure
) throws MalformedURLException
{
1113 if( uriString
== null){
1116 ImageInfo imageInfo
= null;
1118 uriString
= uriString
.replace(" ", "%20"); //replace whitespace
1120 uri
= new URI(uriString
);
1123 imageInfo
= ImageInfo
.NewInstance(uri
, 0);
1125 } catch (Exception e
) {
1126 String message
= "An error occurred when trying to read image meta data for " + uri
.toString() + ": " + e
.getMessage();
1127 logger
.warn(message
);
1128 fireWarningEvent(message
, "unknown location", 2, 0);
1130 ImageFile imageFile
= ImageFile
.NewInstance(uri
, null, imageInfo
);
1131 MediaRepresentation representation
= MediaRepresentation
.NewInstance();
1132 if(imageInfo
!= null){
1133 representation
.setMimeType(imageInfo
.getMimeType());
1134 representation
.setSuffix(imageInfo
.getSuffix());
1136 representation
.addRepresentationPart(imageFile
);
1137 Media media
= isFigure ? Figure
.NewInstance() : Media
.NewInstance();
1138 media
.addRepresentation(representation
);
1140 } catch (URISyntaxException e1
) {
1141 String message
= "An URISyntaxException occurred when trying to create uri from multimedia objcet string: " + uriString
;
1142 logger
.warn(message
);
1143 fireWarningEvent(message
, "unknown location", 4, 0);
1151 * Retrieves an Integer value from a result set. If the value is NULL null is returned.
1152 * ResultSet.getInt() returns 0 therefore we need a special handling for this case.
1156 * @throws SQLException
1158 protected Integer
nullSafeInt(ResultSet rs
, String columnName
) throws SQLException
{
1159 Object intObject
= rs
.getObject(columnName
);
1160 if (intObject
== null){
1163 return Integer
.valueOf(intObject
.toString());
1167 protected Double
nullSafeDouble(ResultSet rs
, String columnName
) throws SQLException
{
1168 Object doubleObject
= rs
.getObject(columnName
);
1169 if (doubleObject
== null){
1172 return Double
.valueOf(doubleObject
.toString());
1176 protected Float
nullSafeFloat(ResultSet rs
, String columnName
) throws SQLException
{
1177 Object doubleObject
= rs
.getObject(columnName
);
1178 if (doubleObject
== null){
1181 return Float
.valueOf(doubleObject
.toString());
1187 * Returns <code>null</code> for all blank strings. Identity function otherwise.
1191 protected String
NB(String str
) {
1192 if (StringUtils
.isBlank(str
)){