830b3931a4b511c18ed83e9bdbbe36e961c09c69
[taxeditor.git] / taxeditor-editor / src / main / java / eu / etaxonomy / taxeditor / editor / DuplicateArbitrator.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.taxeditor.editor;
11
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Set;
15
16 import org.apache.log4j.Logger;
17
18 import eu.etaxonomy.cdm.api.service.ICommonService;
19 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
20 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
21 import eu.etaxonomy.cdm.model.name.NonViralName;
22 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
23 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
24 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
25 import eu.etaxonomy.cdm.strategy.match.DefaultMatchStrategy;
26 import eu.etaxonomy.cdm.strategy.match.IMatchStrategy;
27 import eu.etaxonomy.cdm.strategy.match.MatchException;
28 import eu.etaxonomy.cdm.strategy.match.MatchMode;
29 import eu.etaxonomy.taxeditor.editor.name.NameComposite;
30 import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
31 import eu.etaxonomy.taxeditor.store.CdmStore;
32
33 /**
34 * This class is supposed to handle possible duplications of cdm entities
35 * for a specific editor
36 *
37 * TODO break dependency to editor
38 *
39 * @author n.hoffmann
40 * @created 03.07.2009
41 * @version 1.0
42 *
43 */
44 public class DuplicateArbitrator {
45 private static final Logger logger = Logger.getLogger(DuplicateArbitrator.class);
46
47 /**
48 * The editor this arbitrator should manage possible duplicates for
49 */
50 private MultiPageTaxonEditor editor;
51 private TaxonNameEditor nameEditor;
52 private Set<NameComposite<TaxonBase>> dirtyNameComposites;
53
54 private List<ReferenceBase> duplicateReferences;
55
56 private List<TaxonNameBase> duplicateNames;
57
58 private List<TeamOrPersonBase> duplicateCombinationAuthorTeams;
59 private List<TeamOrPersonBase> duplicateExCombinationAuthorTeams;
60 private List<TeamOrPersonBase> duplicateBasionymAuthorTeams;
61 private List<TeamOrPersonBase> duplicateExBasionymAuthorTeams;
62
63 private ICommonService commonService;
64
65
66 /**
67 * @param adaptableObject
68 */
69 public DuplicateArbitrator(MultiPageTaxonEditor editor) {
70 this.editor = editor;
71
72 commonService = CdmStore.getCommonService();
73
74 nameEditor = (TaxonNameEditor) editor.getPage(Page.NAME);
75
76 duplicateReferences = new ArrayList<ReferenceBase>();
77 duplicateNames = new ArrayList<TaxonNameBase>();
78 duplicateCombinationAuthorTeams = new ArrayList<TeamOrPersonBase>();
79 duplicateExCombinationAuthorTeams = new ArrayList<TeamOrPersonBase>();
80 duplicateBasionymAuthorTeams = new ArrayList<TeamOrPersonBase>();
81 duplicateExBasionymAuthorTeams = new ArrayList<TeamOrPersonBase>();
82 }
83
84 /**
85 * Provide strategies on how to handle possible duplications
86 */
87 public void arbitrate() {
88 // iterate over all names that were edited
89 for(NameComposite composite : nameEditor.getDirtyNames()){
90 logger.warn("Found " + composite + " with possible duplicates");
91
92 NonViralName nonViralName = (NonViralName) composite.getParsedName();
93
94
95 duplicateNames = findMatchingLatinNames(nonViralName);
96 duplicateCombinationAuthorTeams = findMatchingAuthors((TeamOrPersonBase) nonViralName.getCombinationAuthorTeam());
97 duplicateExCombinationAuthorTeams = findMatchingAuthors((TeamOrPersonBase) nonViralName.getExCombinationAuthorTeam());
98 duplicateBasionymAuthorTeams = findMatchingAuthors((TeamOrPersonBase) nonViralName.getBasionymAuthorTeam());
99 duplicateExBasionymAuthorTeams = findMatchingAuthors((TeamOrPersonBase) nonViralName.getExBasionymAuthorTeam());
100 duplicateReferences = findMatchingNomenclaturalReference(nonViralName.getNomenclaturalReference());
101
102 saveNameElements(composite);
103
104 emptyDuplicateSets();
105 }
106 }
107
108 /**
109 *
110 */
111 private void emptyDuplicateSets() {
112 duplicateReferences.clear();
113 duplicateCombinationAuthorTeams.clear();
114 duplicateExCombinationAuthorTeams.clear();
115 duplicateBasionymAuthorTeams.clear();
116 duplicateExBasionymAuthorTeams.clear();
117 duplicateNames.clear();
118 }
119
120 /**
121 * Provides strategies to solve found duplicates
122 * This can either happen automatically or through user input
123 */
124 private void saveNameElements(NameComposite composite) {
125
126 resolveDuplicateNames(composite);
127
128 resolveAllDuplicateAuthors(composite);
129
130 resolveDuplicateReferences(composite);
131
132 // FIXME this is a workaround, because title cache does not get generated correctly in library
133 // remove once #964 is closed
134 composite.getTaxon().setTitleCache((composite.getTaxon().generateTitle()));
135 }
136
137 /**
138 * @param composite
139 */
140 private void resolveDuplicateNames(NameComposite composite) {
141
142 /* When creating a new taxon editor, a taxon with an empty name is created.
143 * We have to delete this empty name explicitly from the session, otherwise it
144 * will be persisted into the database
145 */
146 if(composite.getName().getFullTitleCache().length() == 0){
147 editor.getConversationHolder().delete(composite.getName());
148 }
149
150 if(duplicateNames.size() == 0){
151 // No matches were found for the name part. We can safely
152 // replace the taxons name with the parsed name
153
154 composite.setName(composite.getParsedName());
155 }else if (duplicateNames.size() == 1){
156 composite.setName(duplicateNames.iterator().next());
157 }else{
158
159 // do stuff
160 }
161
162 // debug
163 for(TaxonNameBase name : duplicateNames){
164 logger.warn(name.getFullTitleCache());
165 }
166
167 }
168
169 /**
170 * @param composite
171 */
172 private void resolveDuplicateReferences(NameComposite composite) {
173 if(duplicateReferences.size() == 0){
174 // nomatches found for the reference we replace the reference with the parsed one
175 composite.getName().setNomenclaturalReference(composite.getParsedName().getNomenclaturalReference());
176 }else if(duplicateReferences.size() == 1){
177 // exactly one match. We assume that the user wants this reference
178 composite.getName().setNomenclaturalReference(duplicateReferences.iterator().next());
179 }else{
180 // do stuff
181 }
182
183 // debug
184 for(ReferenceBase reference : duplicateReferences){
185 logger.info(reference.getTitleCache());
186 }
187 }
188
189
190
191 /**
192 * @param composite
193 */
194 private void resolveAllDuplicateAuthors(NameComposite composite) {
195 NonViralName name = HibernateProxyHelper.deproxy(composite.getName(), NonViralName.class);
196 NonViralName parsedName = HibernateProxyHelper.deproxy(composite.getParsedName(), NonViralName.class);
197
198
199 if(duplicateCombinationAuthorTeams.size() == 0){
200 name.setCombinationAuthorTeam(parsedName.getCombinationAuthorTeam());
201 }else if(duplicateCombinationAuthorTeams.size() == 1){
202 name.setCombinationAuthorTeam(duplicateCombinationAuthorTeams.iterator().next());
203 }else{
204 // do stuff
205 }
206
207 if(duplicateExCombinationAuthorTeams.size() == 0){
208 name.setExCombinationAuthorTeam(parsedName.getExCombinationAuthorTeam());
209 }else if(duplicateExCombinationAuthorTeams.size() == 1){
210 name.setExCombinationAuthorTeam(duplicateExCombinationAuthorTeams.iterator().next());
211 }else{
212 // do stuff
213 }
214
215 if(duplicateBasionymAuthorTeams.size() == 0){
216 name.setBasionymAuthorTeam(parsedName.getBasionymAuthorTeam());
217 }else if(duplicateBasionymAuthorTeams.size() == 1){
218 name.setBasionymAuthorTeam(duplicateBasionymAuthorTeams.iterator().next());
219 }else{
220 // do stuff
221 }
222
223 if(duplicateExBasionymAuthorTeams.size() == 0){
224 name.setExBasionymAuthorTeam(parsedName.getExBasionymAuthorTeam());
225 }else if(duplicateExBasionymAuthorTeams.size() == 1){
226 name.setExBasionymAuthorTeam(duplicateExBasionymAuthorTeams.iterator().next());
227 }else{
228 // do stuff
229 }
230
231
232 // debug
233 for(TeamOrPersonBase authorTeam : duplicateCombinationAuthorTeams){
234 logger.info(authorTeam.getTitleCache());
235 }
236 // debug
237 for(TeamOrPersonBase authorTeam : duplicateExCombinationAuthorTeams){
238 logger.info(authorTeam.getTitleCache());
239 }
240 // debug
241 for(TeamOrPersonBase authorTeam : duplicateBasionymAuthorTeams){
242 logger.info(authorTeam.getTitleCache());
243 }
244 // debug
245 for(TeamOrPersonBase authorTeam : duplicateExBasionymAuthorTeams){
246 logger.info(authorTeam.getTitleCache());
247 }
248 }
249
250
251 /**
252 * @param name
253 */
254 private List<ReferenceBase> findMatchingNomenclaturalReference(ReferenceBase referenceBase) {
255 if(referenceBase == null) return new ArrayList<ReferenceBase>();
256 try{
257 return commonService.findMatching(referenceBase, null);
258 }catch (MatchException e) {
259 logger.error("Error finding matching references", e);
260 }
261 return null;
262 }
263
264 /**
265 * @param name
266 */
267 private List<TeamOrPersonBase> findMatchingAuthors(TeamOrPersonBase authorTeam) {
268
269 if(authorTeam == null){
270 return new ArrayList<TeamOrPersonBase>();
271 }
272
273 try{
274 return commonService.findMatching(authorTeam, null);
275 }catch (MatchException e) {
276 logger.error("Error finding matching authors", e);
277 }
278 return null;
279 }
280
281 /**
282 * @param name
283 */
284 private List<TaxonNameBase> findMatchingLatinNames(TaxonNameBase taxonNameBase) {
285
286 try {
287 IMatchStrategy strategy = DefaultMatchStrategy.NewInstance(NonViralName.class);
288 strategy.setMatchMode("nomenclaturalReference", MatchMode.IGNORE);
289 strategy.setMatchMode("combinationAuthorTeam", MatchMode.IGNORE);
290 strategy.setMatchMode("exCombinationAuthorTeam", MatchMode.IGNORE);
291 strategy.setMatchMode("basionymAuthorTeam", MatchMode.IGNORE);
292 strategy.setMatchMode("exBasionymAuthorTeam", MatchMode.IGNORE);
293
294 return commonService.findMatching(taxonNameBase, strategy);
295
296 } catch (MatchException e) {
297 logger.error("Error finding matching names", e);
298 }
299 return null;
300 }
301 }