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