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
.OriginalSourceType
;
47 import eu
.etaxonomy
.cdm
.model
.common
.Representation
;
48 import eu
.etaxonomy
.cdm
.model
.common
.TermVocabulary
;
49 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
50 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
51 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
52 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
53 import eu
.etaxonomy
.cdm
.model
.description
.Modifier
;
54 import eu
.etaxonomy
.cdm
.model
.description
.PresenceTerm
;
55 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
56 import eu
.etaxonomy
.cdm
.model
.description
.State
;
57 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
58 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
59 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
60 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
61 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
62 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaLevel
;
63 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
64 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
65 import eu
.etaxonomy
.cdm
.model
.media
.ImageFile
;
66 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
67 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
68 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
69 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
70 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
71 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
72 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
73 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
74 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
75 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
76 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
77 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
84 public abstract class CdmImportBase
<CONFIG
extends IImportConfigurator
, STATE
extends ImportStateBase
> extends CdmIoBase
<STATE
> implements ICdmImport
<CONFIG
, STATE
>{
85 private static Logger logger
= Logger
.getLogger(CdmImportBase
.class);
87 protected static final boolean CREATE
= true;
88 protected static final boolean IMAGE_GALLERY
= true;
89 protected static final boolean READ_MEDIA_DATA
= true;
91 public static final UUID uuidUserDefinedNamedAreaLevelVocabulary
= UUID
.fromString("255144da-8d95-457e-a327-9752a8f85e5a");
92 public static final UUID uuidUserDefinedNamedAreaVocabulary
= UUID
.fromString("b2238399-a3af-4f6d-b7eb-ff5d0899bf1b");
93 public static final UUID uuidUserDefinedExtensionTypeVocabulary
= UUID
.fromString("e28c1394-1be8-4847-8b81-ab44eb6d5bc8");
94 public static final UUID uuidUserDefinedReferenceSystemVocabulary
= UUID
.fromString("467591a3-10b4-4bf1-9239-f06ece33e90a");
95 public static final UUID uuidUserDefinedFeatureVocabulary
= UUID
.fromString("fe5fccb3-a2f2-4b97-b199-6e2743cf1627");
96 public static final UUID uuidUserDefinedMeasurementUnitVocabulary
= UUID
.fromString("d5e72bb7-f312-4080-bb86-c695d04a6e66");
97 public static final UUID uuidUserDefinedStatisticalMeasureVocabulary
= UUID
.fromString("62a89836-c730-4b4f-a904-3d859dbfc400");
98 public static final UUID uuidUserDefinedStateVocabulary
= UUID
.fromString("f7cddb49-8392-4db1-8640-65b48a0e6d13");
99 public static final UUID uuidUserDefinedTaxonRelationshipTypeVocabulary
= UUID
.fromString("31a324dc-408d-4877-891f-098db21744c6");
100 public static final UUID uuidUserDefinedAnnotationTypeVocabulary
= UUID
.fromString("cd9ecdd2-9cae-4890-9032-ad83293ae883");
101 public static final UUID uuidUserDefinedMarkerTypeVocabulary
= UUID
.fromString("5f02a261-fd7d-4fce-bbe4-21472de8cd51");
102 public static final UUID uuidUserDefinedRankVocabulary
= UUID
.fromString("4dc57931-38e2-46c3-974d-413b087646ba");
104 public static final UUID uuidUserDefinedModifierVocabulary
= UUID
.fromString("2a8b3838-3a95-49ea-9ab2-3049614b5884");
108 private static final String UuidOnly
= "UUIDOnly";
109 private static final String UuidLabel
= "UUID or label";
110 private static final String UuidLabelAbbrev
= "UUID, label or abbreviation";
111 private static final String UuidAbbrev
= "UUID or abbreviation";
113 public enum TermMatchMode
{
114 UUID_ONLY(0, UuidOnly
)
115 ,UUID_LABEL(1, UuidLabel
)
116 ,UUID_LABEL_ABBREVLABEL(2, UuidLabelAbbrev
)
117 ,UUID_ABBREVLABEL(3, UuidAbbrev
)
122 private String representation
;
123 private TermMatchMode(int id
, String representation
){
125 this.representation
= representation
;
130 public String
getRepresentation() {
131 return representation
;
133 public TermMatchMode
valueOf(int id
){
135 case 0: return UUID_ONLY
;
136 case 1: return UUID_LABEL
;
137 case 2: return UUID_LABEL_ABBREVLABEL
;
138 case 3: return UUID_ABBREVLABEL
;
139 default: return UUID_ONLY
;
146 protected Classification
makeTree(STATE state
, Reference reference
){
147 String treeName
= "Classification (Import)";
148 if (reference
!= null && StringUtils
.isNotBlank(reference
.getTitleCache())){
149 treeName
= reference
.getTitleCache();
151 Classification tree
= Classification
.NewInstance(treeName
);
152 tree
.setReference(reference
);
155 // use defined uuid for first tree
156 CONFIG config
= (CONFIG
)state
.getConfig();
157 if (state
.countTrees() < 1 ){
158 tree
.setUuid(config
.getClassificationUuid());
160 getClassificationService().save(tree
);
161 state
.putTree(reference
, tree
);
167 * Alternative memory saving method variant of
168 * {@link #makeTree(STATE state, Reference ref)} which stores only the
169 * UUID instead of the full tree in the <code>ImportStateBase</code> by
170 * using <code>state.putTreeUuid(ref, tree);</code>
176 protected Classification
makeTreeMemSave(STATE state
, Reference ref
){
177 String treeName
= "Classification (Import)";
178 if (ref
!= null && StringUtils
.isNotBlank(ref
.getTitleCache())){
179 treeName
= ref
.getTitleCache();
181 Classification tree
= Classification
.NewInstance(treeName
);
182 tree
.setReference(ref
);
185 // use defined uuid for first tree
186 CONFIG config
= (CONFIG
)state
.getConfig();
187 if (state
.countTrees() < 1 ){
188 tree
.setUuid(config
.getClassificationUuid());
190 getClassificationService().save(tree
);
191 state
.putTreeUuid(ref
, tree
);
196 protected ExtensionType
getExtensionType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
){
197 return getExtensionType(state
, uuid
, label
, text
, labelAbbrev
, null);
199 protected ExtensionType
getExtensionType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<ExtensionType
> voc
){
201 uuid
= UUID
.randomUUID();
203 ExtensionType extensionType
= state
.getExtensionType(uuid
);
204 if (extensionType
== null){
205 extensionType
= (ExtensionType
)getTermService().find(uuid
);
206 if (extensionType
== null){
207 extensionType
= ExtensionType
.NewInstance(text
, label
, labelAbbrev
);
208 extensionType
.setUuid(uuid
);
210 boolean isOrdered
= false;
211 voc
= getVocabulary(uuidUserDefinedExtensionTypeVocabulary
, "User defined vocabulary for extension types", "User Defined Extension Types", null, null, isOrdered
, extensionType
);
213 voc
.addTerm(extensionType
);
214 getTermService().saveOrUpdate(extensionType
);
216 state
.putExtensionType(extensionType
);
218 return extensionType
;
222 protected MarkerType
getMarkerType(STATE state
, String keyString
) {
223 IInputTransformer transformer
= state
.getTransformer();
224 MarkerType markerType
= null;
226 markerType
= transformer
.getMarkerTypeByKey(keyString
);
227 } catch (UndefinedTransformerMethodException e
) {
228 logger
.info("getMarkerTypeByKey not yet implemented for this import");
230 if (markerType
== null ){
233 uuid
= transformer
.getMarkerTypeUuid(keyString
);
234 return getMarkerType(state
, uuid
, keyString
, keyString
, keyString
);
235 } catch (UndefinedTransformerMethodException e
) {
236 logger
.warn("getMarkerTypeUuid not yet implemented for this import");
242 protected MarkerType
getMarkerType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
){
243 return getMarkerType(state
, uuid
, label
, text
, labelAbbrev
, null);
247 protected MarkerType
getMarkerType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<MarkerType
> voc
){
249 uuid
= UUID
.randomUUID();
251 MarkerType markerType
= state
.getMarkerType(uuid
);
252 if (markerType
== null){
253 markerType
= (MarkerType
)getTermService().find(uuid
);
254 if (markerType
== null){
255 markerType
= MarkerType
.NewInstance(label
, text
, labelAbbrev
);
256 markerType
.setUuid(uuid
);
258 boolean isOrdered
= false;
259 voc
= getVocabulary(uuidUserDefinedMarkerTypeVocabulary
, "User defined vocabulary for marker types", "User Defined Marker Types", null, null, isOrdered
, markerType
);
261 voc
.addTerm(markerType
);
262 getTermService().save(markerType
);
264 state
.putMarkerType(markerType
);
269 protected AnnotationType
getAnnotationType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<AnnotationType
> voc
){
271 uuid
= UUID
.randomUUID();
273 AnnotationType annotationType
= state
.getAnnotationType(uuid
);
274 if (annotationType
== null){
275 annotationType
= (AnnotationType
)getTermService().find(uuid
);
276 if (annotationType
== null){
277 annotationType
= AnnotationType
.NewInstance(label
, text
, labelAbbrev
);
278 annotationType
.setUuid(uuid
);
280 boolean isOrdered
= false;
281 voc
= getVocabulary(uuidUserDefinedAnnotationTypeVocabulary
, "User defined vocabulary for annotation types", "User Defined Annotation Types", null, null, isOrdered
, annotationType
);
284 voc
.addTerm(annotationType
);
285 getTermService().save(annotationType
);
287 state
.putAnnotationType(annotationType
);
289 return annotationType
;
293 protected ReferenceSystem
getReferenceSystem(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary voc
){
295 uuid
= UUID
.randomUUID();
297 ReferenceSystem refSystem
= state
.getReferenceSystem(uuid
);
298 if (refSystem
== null){
299 refSystem
= (ReferenceSystem
)getTermService().find(uuid
);
300 if (refSystem
== null){
301 refSystem
= ReferenceSystem
.NewInstance(text
, label
, labelAbbrev
);
303 boolean isOrdered
= false;
304 voc
= getVocabulary(uuidUserDefinedReferenceSystemVocabulary
, "User defined vocabulary for reference systems", "User Defined Reference System", null, null, isOrdered
, refSystem
);
306 voc
.addTerm(refSystem
);
307 refSystem
.setUuid(uuid
);
308 getTermService().save(refSystem
);
310 state
.putReferenceSystem(refSystem
);
318 protected Rank
getRank(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
,OrderedTermVocabulary
<Rank
> voc
, Rank lowerRank
){
320 uuid
= UUID
.randomUUID();
322 Rank rank
= state
.getRank(uuid
);
324 rank
= (Rank
)getTermService().find(uuid
);
326 rank
= new Rank(text
, label
, labelAbbrev
);
328 boolean isOrdered
= true;
329 voc
= (OrderedTermVocabulary
)getVocabulary(uuidUserDefinedRankVocabulary
, "User defined vocabulary for ranks", "User Defined Reference System", null, null, isOrdered
, rank
);
331 if (lowerRank
== null){
334 voc
.addTermAbove(rank
, lowerRank
);
337 getTermService().save(rank
);
346 * Returns a named area for a given uuid by first . If the named area does not
356 protected NamedArea
getNamedArea(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, NamedAreaType areaType
, NamedAreaLevel level
){
357 return getNamedArea(state
, uuid
, label
, text
, labelAbbrev
, areaType
, level
, null, null);
360 protected NamedArea
getNamedArea(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, NamedAreaType areaType
, NamedAreaLevel level
, TermVocabulary voc
, TermMatchMode matchMode
){
361 Class
<NamedArea
> clazz
= NamedArea
.class;
363 uuid
= UUID
.randomUUID();
365 if (matchMode
== null){
366 matchMode
= TermMatchMode
.UUID_ONLY
;
368 NamedArea namedArea
= state
.getNamedArea(uuid
);
369 if (namedArea
== null){
370 DefinedTermBase
<?
> term
= getTermService().find(uuid
);
371 namedArea
= CdmBase
.deproxy(term
,NamedArea
.class);
372 //TODO matching still experimental
373 if (namedArea
== null && (matchMode
.equals(TermMatchMode
.UUID_LABEL
) || matchMode
.equals(TermMatchMode
.UUID_LABEL_ABBREVLABEL
))){
375 Pager
<NamedArea
> areaPager
= (Pager
)getTermService().findByTitle(clazz
, label
, null, null, null, null, null, null);
376 namedArea
= findBestMatchingArea(areaPager
, uuid
, label
, text
, labelAbbrev
, areaType
, level
, voc
);
378 if (namedArea
== null && (matchMode
.equals(TermMatchMode
.UUID_ABBREVLABEL
) || matchMode
.equals(TermMatchMode
.UUID_LABEL_ABBREVLABEL
))){
379 Pager
<NamedArea
> areaPager
= getTermService().findByRepresentationAbbreviation(labelAbbrev
, clazz
, null, null);
380 namedArea
= findBestMatchingArea(areaPager
, uuid
, label
, text
, labelAbbrev
, areaType
, level
, voc
);
383 if (namedArea
== null){
384 namedArea
= NamedArea
.NewInstance(text
, label
, labelAbbrev
);
386 boolean isOrdered
= true;
387 voc
= getVocabulary(uuidUserDefinedNamedAreaVocabulary
, "User defined vocabulary for named areas", "User Defined Named Areas", null, null, isOrdered
, namedArea
);
389 voc
.addTerm(namedArea
);
390 namedArea
.setType(areaType
);
391 namedArea
.setLevel(level
);
392 namedArea
.setUuid(uuid
);
393 getTermService().save(namedArea
);
395 state
.putNamedArea(namedArea
);
401 private NamedArea
findBestMatchingArea(Pager
<NamedArea
> areaPager
, UUID uuid
, String label
, String text
, String abbrev
,
402 NamedAreaType areaType
, NamedAreaLevel level
, TermVocabulary voc
) {
403 // TODO preliminary implementation
404 List
<NamedArea
> list
= areaPager
.getRecords();
405 if (list
.size() == 0){
407 }else if (list
.size() == 1){
409 }else if (list
.size() > 1){
410 String message
= "There is more than 1 matching area for %s, %s, %s. As a preliminary implementation I take the first";
411 message
= String
.format(message
, label
, abbrev
, text
);
412 logger
.warn(message
);
419 protected NamedAreaLevel
getNamedAreaLevel(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<NamedAreaLevel
> voc
){
421 uuid
= UUID
.randomUUID();
423 NamedAreaLevel namedAreaLevel
= state
.getNamedAreaLevel(uuid
);
424 if (namedAreaLevel
== null){
425 //TODO propPath just for testing
426 List
<String
> propPath
= Arrays
.asList("vocabulary");
427 DefinedTermBase
<NamedAreaLevel
> term
= getTermService().load(uuid
, propPath
);
428 namedAreaLevel
= CdmBase
.deproxy(term
, NamedAreaLevel
.class);
429 if (namedAreaLevel
== null){
430 namedAreaLevel
= NamedAreaLevel
.NewInstance(text
, label
, labelAbbrev
);
432 boolean isOrdered
= true;
433 voc
= getVocabulary(uuidUserDefinedNamedAreaLevelVocabulary
, "User defined vocabulary for named area levels", "User Defined Named Area Levels", null, null, isOrdered
, namedAreaLevel
);
435 //FIXME only for debugging
436 Set
<NamedAreaLevel
> terms
= voc
.getTerms();
437 for (NamedAreaLevel level
: terms
){
438 TermVocabulary
<NamedAreaLevel
> levelVoc
= level
.getVocabulary();
439 if (levelVoc
== null){
440 logger
.error("ONLY FOR DEBUG: Level voc is null");
442 logger
.info("ONLY FOR DEBUG: Level voc is not null");
445 voc
.addTerm(namedAreaLevel
);
446 namedAreaLevel
.setUuid(uuid
);
447 getTermService().save(namedAreaLevel
);
449 state
.putNamedAreaLevel(namedAreaLevel
);
451 return namedAreaLevel
;
455 * Returns a {@link State} if it exists. <code>null</code> otherwise.
458 * @return {@link State}
460 protected State
getStateTerm(STATE state
, UUID uuid
){
461 return getStateTerm(state
, uuid
, null, null, null, null);
466 * Returns a {@link State} for a given uuid by first checking if the uuid has already been used in this import, if not
467 * checking if the state exists in the database, if not creating it anew (with vocabulary etc.).
468 * If label, text and labelAbbrev are all <code>null</code> no state is created.
477 protected State
getStateTerm(STATE importState
, UUID uuid
, String label
, String text
, String labelAbbrev
, OrderedTermVocabulary
<State
> voc
) {
481 State stateTerm
= importState
.getStateTerm(uuid
);
482 if (stateTerm
== null){
483 stateTerm
= CdmBase
.deproxy(getTermService().find(uuid
), State
.class);
484 if (stateTerm
== null && ! hasNoLabel(label
, text
, labelAbbrev
)){
485 stateTerm
= State
.NewInstance(text
, label
, labelAbbrev
);
486 stateTerm
.setUuid(uuid
);
488 boolean isOrdered
= true;
489 TermVocabulary
<State
> orderedVoc
= getVocabulary(uuidUserDefinedStateVocabulary
, "User defined vocabulary for states used by Categorical Data", "User Defined States", null, null, isOrdered
, stateTerm
);
490 voc
= CdmBase
.deproxy(orderedVoc
, OrderedTermVocabulary
.class);
492 voc
.addTerm(stateTerm
);
493 getTermService().save(stateTerm
);
495 logger
.warn("No label provided for new state with uuid " + uuid
);
497 importState
.putStateTerm(stateTerm
);
503 * Returns a feature if it exists, null otherwise.
504 * @see #getFeature(ImportStateBase, UUID, String, String, String, TermVocabulary)
509 protected Feature
getFeature(STATE state
, UUID uuid
){
510 return getFeature(state
, uuid
, null, null, null, null);
514 * Returns a feature for a given uuid by first checking if the uuid has already been used in this import, if not
515 * checking if the feature exists in the database, if not creating it anew (with vocabulary etc.).
516 * If label, text and labelAbbrev are all <code>null</code> no feature is created.
524 protected Feature
getFeature(STATE state
, UUID uuid
, String label
, String description
, String labelAbbrev
, TermVocabulary
<Feature
> voc
){
528 Feature feature
= state
.getFeature(uuid
);
529 if (feature
== null){
530 feature
= (Feature
)getTermService().find(uuid
);
531 if (feature
== null && ! hasNoLabel(label
, description
, labelAbbrev
)){
532 feature
= Feature
.NewInstance(description
, label
, labelAbbrev
);
533 feature
.setUuid(uuid
);
534 feature
.setSupportsTextData(true);
535 // UUID uuidFeatureVoc = UUID.fromString("b187d555-f06f-4d65-9e53-da7c93f8eaa8");
537 boolean isOrdered
= false;
538 voc
= getVocabulary(uuidUserDefinedFeatureVocabulary
, "User defined vocabulary for features", "User Defined Features", null, null, isOrdered
, feature
);
540 voc
.addTerm(feature
);
541 getTermService().save(feature
);
543 state
.putFeature(feature
);
549 * Returns a {@link MeasurementUnit} for a given uuid by first checking if the uuid has already been used in this import, if not
550 * checking if the {@link MeasurementUnit} exists in the database, if not creating it anew (with vocabulary etc.).
551 * If label, text and labelAbbrev are all <code>null</code> no {@link MeasurementUnit} is created.
559 protected MeasurementUnit
getMeasurementUnit(STATE state
, UUID uuid
, String label
, String description
, String labelAbbrev
, TermVocabulary
<MeasurementUnit
> voc
){
563 MeasurementUnit unit
= state
.getMeasurementUnit(uuid
);
565 unit
= (MeasurementUnit
)getTermService().find(uuid
);
566 if (unit
== null && ! hasNoLabel(label
, description
, labelAbbrev
)){
567 unit
= MeasurementUnit
.NewInstance(description
, label
, labelAbbrev
);
570 boolean isOrdered
= false;
571 voc
= getVocabulary(uuidUserDefinedMeasurementUnitVocabulary
, "User defined vocabulary for measurement units", "User Defined Measurement Units", null, null, isOrdered
, unit
);
574 getTermService().save(unit
);
576 state
.putMeasurementUnit(unit
);
582 * Returns a {@link StatisticalMeasure} for a given uuid by first checking if the uuid has already been used in this import, if not
583 * checking if the {@link StatisticalMeasure} exists in the database, if not creating it anew (with vocabulary etc.).
584 * If label, text and labelAbbrev are all <code>null</code> no {@link StatisticalMeasure} is created.
592 protected StatisticalMeasure
getStatisticalMeasure(STATE state
, UUID uuid
, String label
, String description
, String labelAbbrev
, TermVocabulary
<StatisticalMeasure
> voc
){
596 StatisticalMeasure statisticalMeasure
= state
.getStatisticalMeasure(uuid
);
597 if (statisticalMeasure
== null){
598 statisticalMeasure
= (StatisticalMeasure
)getTermService().find(uuid
);
599 if (statisticalMeasure
== null && ! hasNoLabel(label
, description
, labelAbbrev
)){
600 statisticalMeasure
= StatisticalMeasure
.NewInstance(description
, label
, labelAbbrev
);
601 statisticalMeasure
.setUuid(uuid
);
603 boolean isOrdered
= false;
604 voc
= getVocabulary(uuidUserDefinedStatisticalMeasureVocabulary
, "User defined vocabulary for statistical measures", "User Defined Statistical Measures", null, null, isOrdered
, statisticalMeasure
);
606 voc
.addTerm(statisticalMeasure
);
607 getTermService().save(statisticalMeasure
);
609 state
.putStatisticalMeasure(statisticalMeasure
);
611 return statisticalMeasure
;
615 * Returns a {@link Modifier} for a given uuid by first checking if the uuid has already been used in this import, if not
616 * checking if the {@link Modifier} exists in the database, if not creating it anew (with vocabulary etc.).
617 * If label, text and labelAbbrev are all <code>null</code> no {@link Modifier} is created.
625 protected Modifier
getModifier(STATE state
, UUID uuid
, String label
, String description
, String labelAbbrev
, TermVocabulary
<Modifier
> voc
){
629 Modifier modifier
= state
.getModifier(uuid
);
630 if (modifier
== null){
631 modifier
= (Modifier
)getTermService().find(uuid
);
632 if (modifier
== null && ! hasNoLabel(label
, description
, labelAbbrev
)){
633 modifier
= Modifier
.NewInstance(description
, label
, labelAbbrev
);
634 modifier
.setUuid(uuid
);
636 boolean isOrdered
= false;
637 voc
= getVocabulary(uuidUserDefinedModifierVocabulary
, "User defined vocabulary for modifier", "User Defined Modifier", null, null, isOrdered
, modifier
);
639 voc
.addTerm(modifier
);
640 getTermService().save(modifier
);
642 state
.putModifier(modifier
);
648 * Returns a taxon relationship type for a given uuid by first checking if the uuid has already been used in this import, if not
649 * checking if the taxon relationship type exists in the database, if not creating it anew (with vocabulary etc.).
650 * If label, text and labelAbbrev are all <code>null</code> no taxon relationship type is created.
658 protected TaxonRelationshipType
getTaxonRelationshipType(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary
<TaxonRelationshipType
> voc
){
662 TaxonRelationshipType relType
= state
.getTaxonRelationshipType(uuid
);
663 if (relType
== null){
664 relType
= (TaxonRelationshipType
)getTermService().find(uuid
);
665 if (relType
== null && ! hasNoLabel(label
, text
, labelAbbrev
)){
666 relType
= new TaxonRelationshipType();
667 Representation repr
= Representation
.NewInstance(text
, label
, labelAbbrev
, Language
.DEFAULT());
668 relType
.addRepresentation(repr
);
669 relType
.setUuid(uuid
);
671 boolean isOrdered
= true;
672 voc
= getVocabulary(uuidUserDefinedTaxonRelationshipTypeVocabulary
, "User defined vocabulary for taxon relationship types", "User Defined Taxon Relationship Types", null, null, isOrdered
, relType
);
674 voc
.addTerm(relType
);
675 getTermService().save(relType
);
677 state
.putTaxonRelationshipType(relType
);
682 private boolean hasNoLabel(String label
, String text
, String labelAbbrev
) {
683 return label
== null && text
== null && labelAbbrev
== null;
688 * Returns a presence term for a given uuid by first ...
696 protected PresenceTerm
getPresenceTerm(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
){
700 PresenceTerm presenceTerm
= state
.getPresenceTerm(uuid
);
701 if (presenceTerm
== null){
702 presenceTerm
= (PresenceTerm
)getTermService().find(uuid
);
703 if (presenceTerm
== null){
704 presenceTerm
= PresenceTerm
.NewInstance(text
, label
, labelAbbrev
);
705 presenceTerm
.setUuid(uuid
);
706 //set vocabulary ; FIXME use another user-defined vocabulary
707 UUID uuidPresenceVoc
= UUID
.fromString("adbbbe15-c4d3-47b7-80a8-c7d104e53a05");
708 TermVocabulary
<PresenceTerm
> voc
= getVocabularyService().find(uuidPresenceVoc
);
709 voc
.addTerm(presenceTerm
);
710 getTermService().save(presenceTerm
);
712 state
.putPresenceTerm(presenceTerm
);
718 * Returns a language for a given uuid by first ...
726 protected Language
getLanguage(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
){
727 return getLanguage(state
, uuid
, label
, text
, labelAbbrev
, null);
730 protected Language
getLanguage(STATE state
, UUID uuid
, String label
, String text
, String labelAbbrev
, TermVocabulary voc
){
734 Language language
= state
.getLanguage(uuid
);
735 if (language
== null){
736 language
= (Language
)getTermService().find(uuid
);
737 if (language
== null){
738 language
= Language
.NewInstance(text
, label
, labelAbbrev
);
740 language
.setUuid(uuid
);
742 UUID uuidLanguageVoc
= UUID
.fromString("463a96f1-20ba-4a4c-9133-854c1682bd9b");
743 boolean isOrdered
= false;
744 voc
= getVocabulary(uuidLanguageVoc
, "User defined languages", "User defined languages", "User defined languages", null, isOrdered
, language
);
746 //set vocabulary ; FIXME use another user-defined vocabulary
748 voc
.addTerm(language
);
749 getTermService().save(language
);
751 state
.putLanguage(language
);
762 protected <T
extends DefinedTermBase
> TermVocabulary
<T
> getVocabulary(UUID uuid
, String text
, String label
, String abbrev
, URI termSourceUri
, boolean isOrdered
, T type
) {
763 List
<String
> propPath
= Arrays
.asList(new String
[]{"terms"});
764 TermVocabulary
<T
> voc
= getVocabularyService().load(uuid
, propPath
);
767 voc
= OrderedTermVocabulary
.NewInstance(text
, label
, abbrev
, termSourceUri
);
769 voc
= TermVocabulary
.NewInstance(text
, label
, abbrev
, termSourceUri
);
772 getVocabularyService().save(voc
);
778 * Adds an orginal source to a sourceable objects (implemented for Identifiable entity and description element.
779 * If cdmBase is not sourceable nothing happens.
780 * TODO Move to DbImportBase once this exists.
781 * TODO also implemented in DbImportObjectCreationMapper (reduce redundance)
784 * @param dbIdAttribute
787 * @throws SQLException
789 public void addOriginalSource(CdmBase cdmBase
, Object idAttributeValue
, String namespace
, Reference citation
) {
790 if (cdmBase
instanceof ISourceable
){
791 IOriginalSource source
;
792 ISourceable sourceable
= (ISourceable
<?
>)cdmBase
;
793 Object id
= idAttributeValue
;
794 String strId
= String
.valueOf(id
);
795 String microCitation
= null;
796 OriginalSourceType type
= OriginalSourceType
.Import
;
797 if (cdmBase
instanceof IdentifiableEntity
){
798 source
= IdentifiableSource
.NewInstance(type
, strId
, namespace
, citation
, microCitation
);
799 }else if (cdmBase
instanceof DescriptionElementBase
){
800 source
= DescriptionElementSource
.NewInstance(type
, strId
, namespace
, citation
, microCitation
);
802 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.");
805 sourceable
.addSource(source
);
806 }else if (cdmBase
!= null){
807 logger
.warn("Sourced object does not implement ISourceable: " + cdmBase
.getClass() + "," + cdmBase
.getUuid());
809 logger
.warn("Sourced object is null");
814 * @see #addOriginalSource(CdmBase, Object, String, Reference)
817 * @param dbIdAttribute
820 * @throws SQLException
822 public void addOriginalSource(ResultSet rs
, CdmBase cdmBase
, String dbIdAttribute
, String namespace
, Reference citation
) throws SQLException
{
823 Object id
= rs
.getObject(dbIdAttribute
);
824 addOriginalSource(cdmBase
, id
, namespace
, citation
);
829 * If the child taxon is missing genus or species epithet information and the rank is below <i>genus</i>
830 * or <i>species</i> respectively the according epithets are taken from the parent taxon.
831 * If the name is an autonym and has no combination author/basionym author the authors are taken from
836 protected void fillMissingEpithetsForTaxa(Taxon parentTaxon
, Taxon childTaxon
) {
837 NonViralName parentName
= HibernateProxyHelper
.deproxy(parentTaxon
.getName(), NonViralName
.class);
838 NonViralName childName
= HibernateProxyHelper
.deproxy(childTaxon
.getName(), NonViralName
.class);
839 fillMissingEpithets(parentName
, childName
);
843 * If the child name is missing genus or species epithet information and the rank is below <i>genus</i>
844 * or <i>species</i> respectively the according epithets are taken from the parent name.
845 * If the name is an autonym and has no combination author/basionym author the authors are taken from
850 protected void fillMissingEpithets(NonViralName parentName
, NonViralName childName
) {
851 if (StringUtils
.isBlank(childName
.getGenusOrUninomial()) && childName
.getRank().isLower(Rank
.GENUS()) ){
852 childName
.setGenusOrUninomial(parentName
.getGenusOrUninomial());
855 if (StringUtils
.isBlank(childName
.getSpecificEpithet()) && childName
.getRank().isLower(Rank
.SPECIES()) ){
856 childName
.setSpecificEpithet(parentName
.getSpecificEpithet());
858 if (childName
.isAutonym() && childName
.getCombinationAuthorTeam() == null && childName
.getBasionymAuthorTeam() == null ){
859 childName
.setCombinationAuthorTeam(parentName
.getCombinationAuthorTeam());
860 childName
.setBasionymAuthorTeam(parentName
.getBasionymAuthorTeam());
865 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
866 * an arbitrary one is chosen.
867 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
868 * is <code>true</code>.
869 * @param createNewIfNotExists
870 * @param isImageGallery if true only taxon description being image galleries are considered.
871 * If false only taxon description being no image galleries are considered.
874 public TaxonNameDescription
getTaxonNameDescription(TaxonNameBase name
, boolean isImageGallery
, boolean createNewIfNotExists
) {
875 Reference
<?
> ref
= null;
876 return getTaxonNameDescription(name
, ref
, isImageGallery
, createNewIfNotExists
);
880 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
881 * Only matches a description if the given reference is a source of the description.<BR>
882 * If a new description is created the given reference will be added as a source.
884 * @see #getTaxonDescription(Taxon, boolean, boolean)
886 public TaxonNameDescription
getTaxonNameDescription(TaxonNameBase
<?
,?
> name
, Reference ref
, boolean isImageGallery
, boolean createNewIfNotExists
) {
887 TaxonNameDescription result
= null;
888 Set
<TaxonNameDescription
> descriptions
= name
.getDescriptions();
889 for (TaxonNameDescription description
: descriptions
){
890 if (description
.isImageGallery() == isImageGallery
){
891 if (hasCorrespondingSource(ref
, description
)){
892 result
= description
;
897 if (result
== null && createNewIfNotExists
){
898 result
= TaxonNameDescription
.NewInstance(name
);
899 result
.setImageGallery(isImageGallery
);
901 result
.addImportSource(null, null, ref
, null);
908 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
909 * an arbitrary one is chosen.
910 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
911 * is <code>true</code>.
912 * @param createNewIfNotExists
913 * @param isImageGallery if true only taxon description being image galleries are considered.
914 * If false only taxon description being no image galleries are considered.
917 public TaxonDescription
getTaxonDescription(Taxon taxon
, boolean isImageGallery
, boolean createNewIfNotExists
) {
918 Reference
<?
> ref
= null;
919 return getTaxonDescription(taxon
, ref
, isImageGallery
, createNewIfNotExists
);
923 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
924 * Only matches a description if the given reference is a source of the description.<BR>
925 * If a new description is created the given reference will be added as a source.
927 * @see #getTaxonDescription(Taxon, boolean, boolean)
929 public TaxonDescription
getTaxonDescription(Taxon taxon
, Reference ref
, boolean isImageGallery
, boolean createNewIfNotExists
) {
930 TaxonDescription result
= null;
931 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
932 for (TaxonDescription description
: descriptions
){
933 if (description
.isImageGallery() == isImageGallery
){
934 if (hasCorrespondingSource(ref
, description
)){
935 result
= description
;
940 if (result
== null && createNewIfNotExists
){
941 result
= TaxonDescription
.NewInstance(taxon
);
942 result
.setImageGallery(isImageGallery
);
944 result
.addImportSource(null, null, ref
, null);
952 * Returns the {@link SpecimenDescription specimen description} for a {@link SpecimenOrObservationBase specimen or observation}.
953 * If there are multiple specimen descriptions an arbitrary one is chosen.
954 * If no specimen description exists, a new one is created if <code>createNewIfNotExists</code> is <code>true</code>.
955 * @param createNewIfNotExists
956 * @param isImageGallery if true only specimen description being image galleries are considered.
957 * If false only specimen description being no image galleries are considered.
960 public SpecimenDescription
getSpecimenDescription(SpecimenOrObservationBase specimen
, boolean isImageGallery
, boolean createNewIfNotExists
) {
961 Reference ref
= null;
962 return getSpecimenDescription(specimen
, ref
, isImageGallery
, createNewIfNotExists
);
966 * Like {@link #getSpecimenDescription(SpecimenOrObservationBase, boolean, boolean)}
967 * Only matches a description if the given reference is a source of the description.<BR>
968 * If a new description is created the given reference will be added as a source.
970 * @see #getTaxonDescription(Taxon, boolean, boolean)
972 public SpecimenDescription
getSpecimenDescription(SpecimenOrObservationBase specimen
, Reference ref
, boolean isImageGallery
, boolean createNewIfNotExists
) {
973 SpecimenDescription result
= null;
974 Set
<SpecimenDescription
> descriptions
= specimen
.getDescriptions();
975 for (SpecimenDescription description
: descriptions
){
976 if (description
.isImageGallery() == isImageGallery
){
977 if (hasCorrespondingSource(ref
, description
)){
978 result
= description
;
983 if (result
== null && createNewIfNotExists
){
984 result
= SpecimenDescription
.NewInstance(specimen
);
985 result
.setImageGallery(isImageGallery
);
987 result
.addImportSource(null, null, ref
, null);
995 * Returns the textdata that holds general information about a feature for a taxon description.
996 * This is mainly necessary for descriptions that have more than one description element for
997 * a given feature such as 'distribution', 'description' or 'common name'. It may also hold
998 * for hierarchical features where no description element exists for a higher hierarchy level.
999 * Example: the description feature has subfeatures. But some information like authorship, figures,
1000 * sources need to be added to the description itself.
1001 * Currently a feature placeholder is marked by a marker of type 'feature placeholder'. Maybe in future
1002 * there will be a boolean marker in the TextData class itself.
1007 * @param createIfNotExists
1010 protected TextData
getFeaturePlaceholder(STATE state
, DescriptionBase
<?
> description
, Feature feature
, boolean createIfNotExists
) {
1011 UUID featurePlaceholderUuid
= MarkupTransformer
.uuidFeaturePlaceholder
;
1012 for (DescriptionElementBase element
: description
.getElements()){
1013 if (element
.isInstanceOf(TextData
.class)){
1014 TextData textData
= CdmBase
.deproxy(element
, TextData
.class);
1015 if (textData
.getFeature() == null || ! textData
.getFeature().equals(feature
)){
1018 for (Marker marker
: textData
.getMarkers()){
1019 MarkerType markerType
= marker
.getMarkerType();
1020 if (markerType
!= null &&
1021 markerType
.getUuid().equals(featurePlaceholderUuid
) &&
1022 marker
.getValue() == true){
1028 if (createIfNotExists
){
1029 TextData newPlaceholder
= TextData
.NewInstance(feature
);
1030 MarkerType placeholderMarkerType
= getMarkerType(state
, featurePlaceholderUuid
, "Feature Placeholder", "Feature Placeholder", null);
1031 Marker marker
= Marker
.NewInstance(placeholderMarkerType
, true);
1032 newPlaceholder
.addMarker(marker
);
1033 description
.addElement(newPlaceholder
);
1034 return newPlaceholder
;
1043 * Returns true, if this description has a source with a citation equal to the given reference.
1044 * Returns true if the given reference is null.
1046 * @param description
1048 private boolean hasCorrespondingSource(Reference
<?
> ref
, DescriptionBase
<?
> description
) {
1050 for (IdentifiableSource source
: description
.getSources()){
1051 if (ref
.equals(source
.getCitation())){
1063 * Returns the accepted taxon of a {@link TaxonBase taxon base}. <BR>
1064 * If taxonBase is of type taxon the same object is returned. If taxonBase is of type
1065 * synonym the accepted taxon is returned if one exists. If no accepted taxon exists
1066 * <code>null</code> is returned. If multiple accepted taxa exist the one taxon with the
1067 * same secundum reference is returned. If no such single taxon exists an
1068 * {@link IllegalStateException illegal state exception} is thrown.
1072 protected Taxon
getAcceptedTaxon(TaxonBase
<?
> taxonBase
) {
1073 if (taxonBase
== null){
1075 }else if(taxonBase
.isInstanceOf(Taxon
.class)){
1076 return CdmBase
.deproxy(taxonBase
, Taxon
.class);
1077 }else if(taxonBase
.isInstanceOf(Synonym
.class)){
1078 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1079 Set
<Taxon
> acceptedTaxa
= synonym
.getAcceptedTaxa();
1080 if (acceptedTaxa
.size() == 0){
1082 }else if (acceptedTaxa
.size() == 1){
1083 return acceptedTaxa
.iterator().next();
1085 Reference
<?
> sec
= synonym
.getSec();
1087 Set
<Taxon
> taxaWithSameSec
= new HashSet
<Taxon
>();
1088 for (Taxon taxon
: acceptedTaxa
){
1089 if (sec
.equals(taxon
.getSec())){
1090 taxaWithSameSec
.add(taxon
);
1093 if (taxaWithSameSec
.size() == 1){
1094 return taxaWithSameSec
.iterator().next();
1097 throw new IllegalStateException("Can't define the one accepted taxon for a synonym out of multiple accept taxa");
1100 throw new IllegalStateException("Unknown TaxonBase subclass: " + taxonBase
.getClass().getName());
1109 * @param readDataFromUrl
1110 * @see #READ_MEDIA_DATA
1112 * @throws MalformedURLException
1114 protected Media
getImageMedia(String uriString
, boolean readMediaData
, boolean isFigure
) throws MalformedURLException
{
1115 if( uriString
== null){
1118 ImageInfo imageInfo
= null;
1120 uriString
= uriString
.replace(" ", "%20"); //replace whitespace
1122 uri
= new URI(uriString
);
1125 imageInfo
= ImageInfo
.NewInstance(uri
, 0);
1127 } catch (Exception e
) {
1128 String message
= "An error occurred when trying to read image meta data for " + uri
.toString() + ": " + e
.getMessage();
1129 logger
.warn(message
);
1130 fireWarningEvent(message
, "unknown location", 2, 0);
1132 ImageFile imageFile
= ImageFile
.NewInstance(uri
, null, imageInfo
);
1133 MediaRepresentation representation
= MediaRepresentation
.NewInstance();
1134 if(imageInfo
!= null){
1135 representation
.setMimeType(imageInfo
.getMimeType());
1136 representation
.setSuffix(imageInfo
.getSuffix());
1138 representation
.addRepresentationPart(imageFile
);
1139 Media media
= isFigure ? Figure
.NewInstance() : Media
.NewInstance();
1140 media
.addRepresentation(representation
);
1142 } catch (URISyntaxException e1
) {
1143 String message
= "An URISyntaxException occurred when trying to create uri from multimedia objcet string: " + uriString
;
1144 logger
.warn(message
);
1145 fireWarningEvent(message
, "unknown location", 4, 0);
1153 * Retrieves an Integer value from a result set. If the value is NULL null is returned.
1154 * ResultSet.getInt() returns 0 therefore we need a special handling for this case.
1158 * @throws SQLException
1160 protected Integer
nullSafeInt(ResultSet rs
, String columnName
) throws SQLException
{
1161 Object intObject
= rs
.getObject(columnName
);
1162 if (intObject
== null){
1165 return Integer
.valueOf(intObject
.toString());
1169 protected Double
nullSafeDouble(ResultSet rs
, String columnName
) throws SQLException
{
1170 Object doubleObject
= rs
.getObject(columnName
);
1171 if (doubleObject
== null){
1174 return Double
.valueOf(doubleObject
.toString());
1178 protected Float
nullSafeFloat(ResultSet rs
, String columnName
) throws SQLException
{
1179 Object doubleObject
= rs
.getObject(columnName
);
1180 if (doubleObject
== null){
1183 return Float
.valueOf(doubleObject
.toString());
1189 * Returns <code>null</code> for all blank strings. Identity function otherwise.
1193 protected String
NB(String str
) {
1194 if (StringUtils
.isBlank(str
)){