latest Markup updates
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / common / CdmImportBase.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9
10 package eu.etaxonomy.cdm.io.common;
11
12 import java.net.MalformedURLException;
13 import java.net.URI;
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;
20 import java.util.Set;
21 import java.util.UUID;
22
23 import org.apache.log4j.Logger;
24
25 import eu.etaxonomy.cdm.api.service.pager.Pager;
26 import eu.etaxonomy.cdm.common.CdmUtils;
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.IOriginalSource;
38 import eu.etaxonomy.cdm.model.common.ISourceable;
39 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
40 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
41 import eu.etaxonomy.cdm.model.common.Language;
42 import eu.etaxonomy.cdm.model.common.Marker;
43 import eu.etaxonomy.cdm.model.common.MarkerType;
44 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
45 import eu.etaxonomy.cdm.model.common.TermVocabulary;
46 import eu.etaxonomy.cdm.model.description.DescriptionBase;
47 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
48 import eu.etaxonomy.cdm.model.description.Feature;
49 import eu.etaxonomy.cdm.model.description.PresenceTerm;
50 import eu.etaxonomy.cdm.model.description.TaxonDescription;
51 import eu.etaxonomy.cdm.model.description.TextData;
52 import eu.etaxonomy.cdm.model.location.NamedArea;
53 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
54 import eu.etaxonomy.cdm.model.location.NamedAreaType;
55 import eu.etaxonomy.cdm.model.location.ReferenceSystem;
56 import eu.etaxonomy.cdm.model.media.ImageFile;
57 import eu.etaxonomy.cdm.model.media.Media;
58 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
59 import eu.etaxonomy.cdm.model.name.NonViralName;
60 import eu.etaxonomy.cdm.model.name.Rank;
61 import eu.etaxonomy.cdm.model.reference.Reference;
62 import eu.etaxonomy.cdm.model.taxon.Classification;
63 import eu.etaxonomy.cdm.model.taxon.Synonym;
64 import eu.etaxonomy.cdm.model.taxon.Taxon;
65 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
66
67 /**
68 * @author a.mueller
69 * @created 01.07.2008
70 * @version 1.0
71 */
72 public abstract class CdmImportBase<CONFIG extends IImportConfigurator, STATE extends ImportStateBase> extends CdmIoBase<STATE> implements ICdmImport<CONFIG, STATE>{
73 private static Logger logger = Logger.getLogger(CdmImportBase.class);
74
75 protected static final boolean CREATE = true;
76 protected static final boolean IMAGE_GALLERY = true;
77
78 public static final UUID uuidUserDefinedNamedAreaLevelVocabulary = UUID.fromString("255144da-8d95-457e-a327-9752a8f85e5a");
79 public static final UUID uuidUserDefinedNamedAreaVocabulary = UUID.fromString("b2238399-a3af-4f6d-b7eb-ff5d0899bf1b");
80 public static final UUID uuidUserDefinedExtensionTypeVocabulary = UUID.fromString("e28c1394-1be8-4847-8b81-ab44eb6d5bc8");
81 public static final UUID uuidUserDefinedReferenceSystemVocabulary = UUID.fromString("467591a3-10b4-4bf1-9239-f06ece33e90a");
82 public static final UUID uuidUserDefinedFeatureVocabulary = UUID.fromString("fe5fccb3-a2f2-4b97-b199-6e2743cf1627");
83 public static final UUID uuidUserDefinedAnnotationTypeVocabulary = UUID.fromString("cd9ecdd2-9cae-4890-9032-ad83293ae883");
84 public static final UUID uuidUserDefinedMarkerTypeVocabulary = UUID.fromString("5f02a261-fd7d-4fce-bbe4-21472de8cd51");
85
86
87 private static final String UuidOnly = "UUIDOnly";
88 private static final String UuidLabel = "UUID or label";
89 private static final String UuidLabelAbbrev = "UUID, label or abbreviation";
90 private static final String UuidAbbrev = "UUID or abbreviation";
91
92 public enum TermMatchMode{
93 UUID_ONLY(0, UuidOnly)
94 ,UUID_LABEL(1, UuidLabel)
95 ,UUID_LABEL_ABBREVLABEL(2, UuidLabelAbbrev)
96 ,UUID_ABBREVLABEL(3, UuidAbbrev)
97 ;
98
99
100 private int id;
101 private String representation;
102 private TermMatchMode(int id, String representation){
103 this.id = id;
104 this.representation = representation;
105 }
106 public int getId() {
107 return id;
108 }
109 public String getRepresentation() {
110 return representation;
111 }
112 public TermMatchMode valueOf(int id){
113 switch (id){
114 case 0: return UUID_ONLY;
115 case 1: return UUID_LABEL;
116 case 2: return UUID_LABEL_ABBREVLABEL;
117 case 3: return UUID_ABBREVLABEL;
118 default: return UUID_ONLY;
119 }
120 }
121
122
123 }
124
125 protected Classification makeTree(STATE state, Reference reference){
126 Reference ref = CdmBase.deproxy(reference, Reference.class);
127 String treeName = "Classification (Import)";
128 if (ref != null && CdmUtils.isNotEmpty(ref.getTitleCache())){
129 treeName = ref.getTitleCache();
130 }
131 Classification tree = Classification.NewInstance(treeName);
132 tree.setReference(ref);
133
134
135 // use defined uuid for first tree
136 CONFIG config = (CONFIG)state.getConfig();
137 if (state.countTrees() < 1 ){
138 tree.setUuid(config.getClassificationUuid());
139 }
140 getClassificationService().save(tree);
141 state.putTree(ref, tree);
142 return tree;
143 }
144
145
146 /**
147 * Alternative memory saving method variant of
148 * {@link #makeTree(STATE state, Reference ref)} which stores only the
149 * UUID instead of the full tree in the <code>ImportStateBase</code> by
150 * using <code>state.putTreeUuid(ref, tree);</code>
151 *
152 * @param state
153 * @param ref
154 * @return
155 */
156 protected Classification makeTreeMemSave(STATE state, Reference ref){
157 String treeName = "Classification (Import)";
158 if (ref != null && CdmUtils.isNotEmpty(ref.getTitleCache())){
159 treeName = ref.getTitleCache();
160 }
161 Classification tree = Classification.NewInstance(treeName);
162 tree.setReference(ref);
163
164
165 // use defined uuid for first tree
166 CONFIG config = (CONFIG)state.getConfig();
167 if (state.countTrees() < 1 ){
168 tree.setUuid(config.getClassificationUuid());
169 }
170 getClassificationService().save(tree);
171 state.putTreeUuid(ref, tree);
172 return tree;
173 }
174
175
176 protected ExtensionType getExtensionType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
177 return getExtensionType(state, uuid, label, text, labelAbbrev, null);
178 }
179 protected ExtensionType getExtensionType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<ExtensionType> voc){
180 if (uuid == null){
181 uuid = UUID.randomUUID();
182 }
183 ExtensionType extensionType = state.getExtensionType(uuid);
184 if (extensionType == null){
185 extensionType = (ExtensionType)getTermService().find(uuid);
186 if (extensionType == null){
187 extensionType = ExtensionType.NewInstance(text, label, labelAbbrev);
188 extensionType.setUuid(uuid);
189 if (voc == null){
190 boolean isOrdered = false;
191 voc = getVocabulary(uuidUserDefinedExtensionTypeVocabulary, "User defined vocabulary for extension types", "User Defined Extension Types", null, null, isOrdered, extensionType);
192 }
193 voc.addTerm(extensionType);
194 getTermService().saveOrUpdate(extensionType);
195 }
196 state.putExtensionType(extensionType);
197 }
198 return extensionType;
199 }
200
201
202 protected MarkerType getMarkerType(STATE state, String keyString) {
203 IInputTransformer transformer = state.getTransformer();
204 MarkerType markerType = null;
205 try {
206 markerType = transformer.getMarkerTypeByKey(keyString);
207 } catch (UndefinedTransformerMethodException e) {
208 logger.info("getMarkerTypeByKey not yet implemented for this import");
209 }
210 if (markerType == null ){
211 UUID uuid;
212 try {
213 uuid = transformer.getMarkerTypeUuid(keyString);
214 return getMarkerType(state, uuid, keyString, keyString, keyString);
215 } catch (UndefinedTransformerMethodException e) {
216 logger.warn("getMarkerTypeUuid not yet implemented for this import");
217 }
218 }
219 return null;
220 }
221
222 protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
223 return getMarkerType(state, uuid, label, text, labelAbbrev, null);
224 }
225
226
227 protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<MarkerType> voc){
228 if (uuid == null){
229 uuid = UUID.randomUUID();
230 }
231 MarkerType markerType = state.getMarkerType(uuid);
232 if (markerType == null){
233 markerType = (MarkerType)getTermService().find(uuid);
234 if (markerType == null){
235 markerType = MarkerType.NewInstance(label, text, labelAbbrev);
236 markerType.setUuid(uuid);
237 if (voc == null){
238 boolean isOrdered = false;
239 voc = getVocabulary(uuidUserDefinedMarkerTypeVocabulary, "User defined vocabulary for marker types", "User Defined Marker Types", null, null, isOrdered, markerType);
240 }
241 voc.addTerm(markerType);
242 getTermService().save(markerType);
243 }
244 state.putMarkerType(markerType);
245 }
246 return markerType;
247 }
248
249 protected AnnotationType getAnnotationType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<AnnotationType> voc){
250 if (uuid == null){
251 uuid = UUID.randomUUID();
252 }
253 AnnotationType annotationType = state.getAnnotationType(uuid);
254 if (annotationType == null){
255 annotationType = (AnnotationType)getTermService().find(uuid);
256 if (annotationType == null){
257 annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
258 annotationType.setUuid(uuid);
259 if (voc == null){
260 boolean isOrdered = false;
261 voc = getVocabulary(uuidUserDefinedAnnotationTypeVocabulary, "User defined vocabulary for annotation types", "User Defined Annotation Types", null, null, isOrdered, annotationType);
262 }
263
264 voc.addTerm(annotationType);
265 getTermService().save(annotationType);
266 }
267 state.putAnnotationType(annotationType);
268 }
269 return annotationType;
270 }
271
272
273 protected ReferenceSystem getReferenceSystem(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary voc){
274 if (uuid == null){
275 uuid = UUID.randomUUID();
276 }
277 ReferenceSystem refSystem = state.getReferenceSystem(uuid);
278 if (refSystem == null){
279 refSystem = (ReferenceSystem)getTermService().find(uuid);
280 if (refSystem == null){
281 refSystem = ReferenceSystem.NewInstance(text, label, labelAbbrev);
282 if (voc == null){
283 boolean isOrdered = false;
284 voc = getVocabulary(uuidUserDefinedReferenceSystemVocabulary, "User defined vocabulary for named areas", "User Defined Reference System", null, null, isOrdered, refSystem);
285 }
286 voc.addTerm(refSystem);
287 refSystem.setUuid(uuid);
288 getTermService().save(refSystem);
289 }
290 state.putReferenceSystem(refSystem);
291 }
292 return refSystem;
293
294 }
295
296 /**
297 * Returns a named area for a given uuid by first . If the named area does not
298 * @param state
299 * @param uuid
300 * @param label
301 * @param text
302 * @param labelAbbrev
303 * @param areaType
304 * @param level
305 * @return
306 */
307 protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level){
308 return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, null, null);
309 }
310
311 protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode){
312 if (uuid == null){
313 uuid = UUID.randomUUID();
314 }
315 if (matchMode == null){
316 matchMode = TermMatchMode.UUID_ONLY;
317 }
318 NamedArea namedArea = state.getNamedArea(uuid);
319 if (namedArea == null){
320 //TODO matching still experimental
321 namedArea = (NamedArea)getTermService().find(uuid);
322 if (namedArea == null && matchMode.equals(TermMatchMode.UUID_LABEL)){
323 logger.warn("UUID_LABEL not yet implemented");
324 }
325 if (namedArea == null && matchMode.equals(TermMatchMode.UUID_ABBREVLABEL)){
326 Pager<NamedArea> areaPager = getTermService().findByRepresentationAbbreviation(labelAbbrev, NamedArea.class, null, null);
327 namedArea = findBestMatchingArea(areaPager, uuid, label, text, labelAbbrev, areaType, level, voc);
328 }
329 if (namedArea == null && matchMode.equals(TermMatchMode.UUID_LABEL_ABBREVLABEL)){
330 logger.warn("UUID_LABEL not yet implemented");
331 }
332
333 if (namedArea == null){
334 namedArea = NamedArea.NewInstance(text, label, labelAbbrev);
335 if (voc == null){
336 boolean isOrdered = true;
337 voc = getVocabulary(uuidUserDefinedNamedAreaVocabulary, "User defined vocabulary for named areas", "User Defined Named Areas", null, null, isOrdered, namedArea);
338 }
339 voc.addTerm(namedArea);
340 namedArea.setType(areaType);
341 namedArea.setLevel(level);
342 namedArea.setUuid(uuid);
343 getTermService().save(namedArea);
344 }
345 state.putNamedArea(namedArea);
346 }
347 return namedArea;
348 }
349
350
351 private NamedArea findBestMatchingArea(Pager<NamedArea> areaPager, UUID uuid, String label, String text, String abbrev,
352 NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc) {
353 // TODO preliminary implementation
354 List<NamedArea> list = areaPager.getRecords();
355 if (list.size() == 0){
356 return null;
357 }else if (list.size() == 1){
358 return list.get(0);
359 }else if (list.size() > 1){
360 String message = "There is more than 1 matching area for %s, %s, %s. As a preliminary implementation I take the first";
361 message = String.format(message, label, abbrev, text);
362 logger.warn(message);
363 return list.get(0);
364 }
365 return null;
366 }
367
368
369 protected NamedAreaLevel getNamedAreaLevel(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<NamedAreaLevel> voc){
370 if (uuid == null){
371 uuid = UUID.randomUUID();
372 }
373 NamedAreaLevel namedAreaLevel = state.getNamedAreaLevel(uuid);
374 if (namedAreaLevel == null){
375 namedAreaLevel = CdmBase.deproxy(getTermService().find(uuid), NamedAreaLevel.class);
376 if (namedAreaLevel == null){
377 namedAreaLevel = NamedAreaLevel.NewInstance(text, label, labelAbbrev);
378 if (voc == null){
379 boolean isOrdered = true;
380 voc = getVocabulary(uuidUserDefinedNamedAreaLevelVocabulary, "User defined vocabulary for named area levels", "User Defined Named Area Levels", null, null, isOrdered, namedAreaLevel);
381 }
382 voc.addTerm(namedAreaLevel);
383 namedAreaLevel.setUuid(uuid);
384 getTermService().save(namedAreaLevel);
385 }
386 state.putNamedAreaLevel(namedAreaLevel);
387 }
388 return namedAreaLevel;
389 }
390
391
392 /**
393 * Returns a feature if it exists, null otherwise.
394 * @see #getFeature(ImportStateBase, UUID, String, String, String, TermVocabulary)
395 * @param state
396 * @param uuid
397 * @return
398 */
399 protected Feature getFeature(STATE state, UUID uuid){
400 return getFeature(state, uuid, null, null, null, null);
401 }
402
403 /**
404 * Returns a feature for a given uuid by first checking if the uuid has already been used in this import, if not
405 * checking if the feature exists in the database, if not creating it anew (with vocabulary etc.).
406 * If label, text and labelAbbrev are all <code>null</code> no feature is created.
407 * @param state
408 * @param uuid
409 * @param label
410 * @param text
411 * @param labelAbbrev
412 * @return
413 */
414 protected Feature getFeature(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<Feature> voc){
415 if (uuid == null){
416 return null;
417 }
418 Feature feature = state.getFeature(uuid);
419 if (feature == null){
420 feature = (Feature)getTermService().find(uuid);
421 if (feature == null && ! hasNoLabel(label, text, labelAbbrev)){
422 feature = Feature.NewInstance(text, label, labelAbbrev);
423 feature.setUuid(uuid);
424 feature.setSupportsTextData(true);
425 // UUID uuidFeatureVoc = UUID.fromString("b187d555-f06f-4d65-9e53-da7c93f8eaa8");
426 if (voc == null){
427 boolean isOrdered = false;
428 voc = getVocabulary(uuidUserDefinedFeatureVocabulary, "User defined vocabulary for features", "User Defined Features", null, null, isOrdered, feature);
429 }
430 voc.addTerm(feature);
431 getTermService().save(feature);
432 }
433 state.putFeature(feature);
434 }
435 return feature;
436 }
437
438 private boolean hasNoLabel(String label, String text, String labelAbbrev) {
439 return label == null && text == null && labelAbbrev == null;
440 }
441
442
443 /**
444 * Returns a presence term for a given uuid by first ...
445 * @param state
446 * @param uuid
447 * @param label
448 * @param text
449 * @param labelAbbrev
450 * @return
451 */
452 protected PresenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev){
453 if (uuid == null){
454 return null;
455 }
456 PresenceTerm presenceTerm = state.getPresenceTerm(uuid);
457 if (presenceTerm == null){
458 presenceTerm = (PresenceTerm)getTermService().find(uuid);
459 if (presenceTerm == null){
460 presenceTerm = PresenceTerm.NewInstance(text, label, labelAbbrev);
461 presenceTerm.setUuid(uuid);
462 //set vocabulary ; FIXME use another user-defined vocabulary
463 UUID uuidPresenceVoc = UUID.fromString("adbbbe15-c4d3-47b7-80a8-c7d104e53a05");
464 TermVocabulary<PresenceTerm> voc = getVocabularyService().find(uuidPresenceVoc);
465 voc.addTerm(presenceTerm);
466 getTermService().save(presenceTerm);
467 }
468 state.putPresenceTerm(presenceTerm);
469 }
470 return presenceTerm;
471 }
472
473 /**
474 * Returns a language for a given uuid by first ...
475 * @param state
476 * @param uuid
477 * @param label
478 * @param text
479 * @param labelAbbrev
480 * @return
481 */
482 protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev){
483 if (uuid == null){
484 return null;
485 }
486 Language language = state.getLanguage(uuid);
487 if (language == null){
488 language = (Language)getTermService().find(uuid);
489 if (language == null){
490 language = Language.NewInstance(text, label, labelAbbrev);
491
492 language.setUuid(uuid);
493 //set vocabulary ; FIXME use another user-defined vocabulary
494 UUID uuidLanguageVoc = UUID.fromString("45ac7043-7f5e-4f37-92f2-3874aaaef2de");
495 TermVocabulary<Language> voc = getVocabularyService().find(uuidLanguageVoc);
496 voc.addTerm(language);
497 getTermService().save(language);
498 }
499 state.putLanguage(language);
500 }
501 return language;
502 }
503
504
505 /**
506 * @param uuid
507 * @return
508 *
509 */
510 protected <T extends DefinedTermBase> TermVocabulary<T> getVocabulary(UUID uuid, String text, String label, String abbrev, URI termSourceUri, boolean isOrdered, T type) {
511 List<String> propPath = Arrays.asList(new String[]{"terms"});
512 TermVocabulary<T> voc = getVocabularyService().load(uuid, propPath);
513 if (voc == null){
514 if (isOrdered){
515 voc = OrderedTermVocabulary.NewInstance(text, label, abbrev, termSourceUri);
516 }else{
517 voc = TermVocabulary.NewInstance(text, label, abbrev, termSourceUri);
518 }
519 voc.setUuid(uuid);
520 getVocabularyService().save(voc);
521 }
522 return voc;
523 }
524
525 /**
526 * Adds an orginal source to a sourceable objects (implemented for Identifiable entity and description element.
527 * If cdmBase is not sourceable nothing happens.
528 * TODO Move to DbImportBase once this exists.
529 * TODO also implemented in DbImportObjectCreationMapper (reduce redundance)
530 * @param rs
531 * @param cdmBase
532 * @param dbIdAttribute
533 * @param namespace
534 * @param citation
535 * @throws SQLException
536 */
537 public void addOriginalSource(CdmBase cdmBase, Object idAttributeValue, String namespace, Reference citation) {
538 if (cdmBase instanceof ISourceable ){
539 IOriginalSource source;
540 ISourceable sourceable = (ISourceable)cdmBase;
541 Object id = idAttributeValue;
542 String strId = String.valueOf(id);
543 String microCitation = null;
544 if (cdmBase instanceof IdentifiableEntity){
545 source = IdentifiableSource.NewInstance(strId, namespace, citation, microCitation);
546 }else if (cdmBase instanceof DescriptionElementBase){
547 source = DescriptionElementSource.NewInstance(strId, namespace, citation, microCitation);
548 }else{
549 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.");
550 return;
551 }
552 sourceable.addSource(source);
553 }else if (cdmBase != null){
554 logger.warn("Sourced object does not implement ISourceable: " + cdmBase.getClass() + "," + cdmBase.getUuid());
555 }else{
556 logger.warn("Sourced object is null");
557 }
558 }
559
560 /**
561 * @see #addOriginalSource(CdmBase, Object, String, Reference)
562 * @param rs
563 * @param cdmBase
564 * @param dbIdAttribute
565 * @param namespace
566 * @param citation
567 * @throws SQLException
568 */
569 public void addOriginalSource(ResultSet rs, CdmBase cdmBase, String dbIdAttribute, String namespace, Reference citation) throws SQLException {
570 Object id = rs.getObject(dbIdAttribute);
571 addOriginalSource(cdmBase, id, namespace, citation);
572 }
573
574
575 /**
576 * If the child taxon is missing genus or species epithet information and the rank is below <i>genus</i>
577 * or <i>species</i> respectively the according epithets are taken from the parent taxon.
578 * If the name is an autonym and has no combination author/basionym author the authors are taken from
579 * the parent.
580 * @param parentTaxon
581 * @param childTaxon
582 */
583 protected void fillMissingEpithetsForTaxa(Taxon parentTaxon, Taxon childTaxon) {
584 NonViralName parentName = HibernateProxyHelper.deproxy(parentTaxon.getName(), NonViralName.class);
585 NonViralName childName = HibernateProxyHelper.deproxy(childTaxon.getName(), NonViralName.class);
586 fillMissingEpithets(parentName, childName);
587 }
588
589 /**
590 * If the child name is missing genus or species epithet information and the rank is below <i>genus</i>
591 * or <i>species</i> respectively the according epithets are taken from the parent name.
592 * If the name is an autonym and has no combination author/basionym author the authors are taken from
593 * the parent.
594 * @param parentTaxon
595 * @param childTaxon
596 */
597 protected void fillMissingEpithets(NonViralName parentName, NonViralName childName) {
598 if (CdmUtils.isEmpty(childName.getGenusOrUninomial()) && childName.getRank().isLower(Rank.GENUS()) ){
599 childName.setGenusOrUninomial(parentName.getGenusOrUninomial());
600 }
601
602 if (CdmUtils.isEmpty(childName.getSpecificEpithet()) && childName.getRank().isLower(Rank.SPECIES()) ){
603 childName.setSpecificEpithet(parentName.getSpecificEpithet());
604 }
605 if (childName.isAutonym() && childName.getCombinationAuthorTeam() == null && childName.getBasionymAuthorTeam() == null ){
606 childName.setCombinationAuthorTeam(parentName.getCombinationAuthorTeam());
607 childName.setBasionymAuthorTeam(parentName.getBasionymAuthorTeam());
608 }
609 }
610
611 /**
612 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
613 * an arbitrary one is chosen.
614 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
615 * is <code>true</code>.
616 * @param createNewIfNotExists
617 * @param isImageGallery if true only taxon description being image galleries are considered.
618 * If false only taxon description being no image galleries are considered.
619 * @return
620 */
621 public TaxonDescription getTaxonDescription(Taxon taxon, boolean isImageGallery, boolean createNewIfNotExists) {
622 Reference ref = null;
623 return getTaxonDescription(taxon, ref, isImageGallery, createNewIfNotExists);
624 }
625
626 /**
627 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
628 * Only matches a description if the given reference is a source of the description.<BR>
629 * If a new description is created the given reference will be added as a source.
630 *
631 * @see #getTaxonDescription(Taxon, boolean, boolean)
632 */
633 public TaxonDescription getTaxonDescription(Taxon taxon, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
634 TaxonDescription result = null;
635 Set<TaxonDescription> descriptions= taxon.getDescriptions();
636 for (TaxonDescription description : descriptions){
637 if (description.isImageGallery() == isImageGallery){
638 if (hasCorrespondingSource(ref, description)){
639 result = description;
640 break;
641 }
642 }
643 }
644 if (result == null && createNewIfNotExists){
645 result = TaxonDescription.NewInstance(taxon);
646 result.setImageGallery(isImageGallery);
647 if (ref != null){
648 result.addSource(null, null, ref, null);
649 }
650 }
651 return result;
652 }
653
654
655 /**
656 * Returns the textdata that holds general information about a feature for a taxon description.
657 * This is mainly necessary for descriptions that have more than one description element for
658 * a given feature such as 'distribution', 'description' or 'common name'. It may also hold
659 * for hierarchical features where no description element exists for a higher hierarchie level.
660 * Example: the description feature has subfeatures. But some information like authorship, figures,
661 * sources need to be added to the description itself.
662 * Currently a feature placeholder is marked by a marker of type 'feature placeholder'. Maybe in future
663 * there will be a boolean marker in the TextData class itself.
664 * @param state
665 * @param feature
666 * @param taxon
667 * @param ref
668 * @param createIfNotExists
669 * @return
670 */
671 protected TextData getFeaturePlaceholder(STATE state, DescriptionBase<?> description, Feature feature, boolean createIfNotExists) {
672 UUID featurePlaceholderUuid = MarkupTransformer.uuidFeaturePlaceholder;
673 for (DescriptionElementBase element : description.getElements()){
674 if (element.isInstanceOf(TextData.class)){
675 TextData textData = CdmBase.deproxy(element, TextData.class);
676 if (textData.getFeature() == null || ! textData.getFeature().equals(feature)){
677 continue;
678 }
679 for (Marker marker : textData.getMarkers()){
680 MarkerType markerType = marker.getMarkerType();
681 if (markerType != null &&
682 markerType.getUuid().equals(featurePlaceholderUuid) &&
683 marker.getValue() == true){
684 return textData;
685 }
686 }
687 }
688 }
689 if (createIfNotExists){
690 TextData newPlaceholder = TextData.NewInstance(feature);
691 MarkerType placeholderMarkerType = getMarkerType(state, featurePlaceholderUuid, "Feature Placeholder", "Feature Placeholder", null);
692 Marker marker = Marker.NewInstance(placeholderMarkerType, true);
693 newPlaceholder.addMarker(marker);
694 description.addElement(newPlaceholder);
695 return newPlaceholder;
696 }else{
697 return null;
698 }
699 }
700
701
702
703 /**
704 * Returns true, if this description has a source with a citation equal to the given reference.
705 * Returns true if the given reference is null.
706 * @param ref
707 * @param description
708 */
709 private boolean hasCorrespondingSource(Reference ref, TaxonDescription description) {
710 if (ref != null){
711 for (IdentifiableSource source : description.getSources()){
712 if (ref.equals(source.getCitation())){
713 return true;
714 }
715 }
716 return false;
717 }
718 return true;
719
720 }
721
722
723 /**
724 * Returns the accepted taxon of a {@link TaxonBase taxon base}. <BR>
725 * If taxonBase is of type taxon the same object is returned. If taxonBase is of type
726 * synonym the accepted taxon is returned if one exists. If no accepted taxon exists
727 * <code>null</code> is returned. If multiple accepted taxa exist the one taxon with the
728 * same secundum reference is returned. If no such single taxon exists an
729 * {@link IllegalStateException illegal state exception} is thrown.
730 * @param taxonBase
731 * @return
732 */
733 protected Taxon getAcceptedTaxon(TaxonBase<?> taxonBase) {
734 if (taxonBase == null){
735 return null;
736 }else if(taxonBase.isInstanceOf(Taxon.class)){
737 return CdmBase.deproxy(taxonBase, Taxon.class);
738 }else if(taxonBase.isInstanceOf(Synonym.class)){
739 Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
740 Set<Taxon> acceptedTaxa = synonym.getAcceptedTaxa();
741 if (acceptedTaxa.size() == 0){
742 return null;
743 }else if (acceptedTaxa.size() == 1){
744 return acceptedTaxa.iterator().next();
745 }else{
746 Reference sec = synonym.getSec();
747 if (sec != null){
748 Set<Taxon> taxaWithSameSec = new HashSet<Taxon>();
749 for (Taxon taxon: acceptedTaxa){
750 if (sec.equals(taxon.getSec())){
751 taxaWithSameSec.add(taxon);
752 }
753 }
754 if (taxaWithSameSec.size() == 1){
755 return taxaWithSameSec.iterator().next();
756 }
757 }
758 throw new IllegalStateException("Can't define the one accepted taxon for a synonym out of multiple accept taxa");
759 }
760 }else{
761 throw new IllegalStateException("Unknown TaxonBase subclass: " + taxonBase.getClass().getName());
762 }
763 }
764
765
766
767 /**
768 * @param derivedUnitFacade
769 * @param multimediaObject
770 * @throws MalformedURLException
771 */
772 protected Media getImageMedia(String multimediaObject, boolean readDataFromUrl) throws MalformedURLException {
773 if( multimediaObject == null){
774 return null;
775 } else {
776 ImageInfo imageInfo = null;
777 URI uri;
778 try {
779 uri = new URI(multimediaObject);
780 try {
781 if (readDataFromUrl){
782 imageInfo = ImageInfo.NewInstance(uri, 0);
783 }
784 } catch (Exception e) {
785 String message = "An error occurred when trying to read image meta data: " + e.getMessage();
786 logger.warn(message);
787 }
788 ImageFile imageFile = ImageFile.NewInstance(uri, null, imageInfo);
789 MediaRepresentation representation = MediaRepresentation.NewInstance();
790 if(imageInfo != null){
791 representation.setMimeType(imageInfo.getMimeType());
792 }
793 representation.addRepresentationPart(imageFile);
794 Media media = Media.NewInstance();
795 media.addRepresentation(representation);
796 return media;
797 } catch (URISyntaxException e1) {
798 String message = "An URISyntaxException occurred when trying to create uri from multimedia objcet string: " + multimediaObject;
799 logger.warn(message);
800 return null;
801 }
802 }
803 }
804
805
806 }