tabbed_properties branch merged into trunk completely
[taxeditor.git] / taxeditor-store / src / main / java / eu / etaxonomy / taxeditor / parser / ParseHandler.java
1 /**
2 *
3 */
4 package eu.etaxonomy.taxeditor.parser;
5
6 import java.lang.reflect.Method;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import org.apache.log4j.Logger;
11 import org.eclipse.swt.widgets.Control;
12
13 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
14 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
15 import eu.etaxonomy.cdm.model.name.NonViralName;
16 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
17 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
18 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
19 import eu.etaxonomy.cdm.strategy.match.MatchException;
20 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
21 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
22 import eu.etaxonomy.taxeditor.store.CdmStore;
23
24 /**
25 * @author n.hoffmann
26 *
27 */
28 public class ParseHandler{
29 private static final Logger logger = Logger.getLogger(ParseHandler.class);
30
31 private TaxonNameBase taxonNameBase;
32
33 private List<INomenclaturalReference> duplicateReferences;
34 private List<INomenclaturalReference> duplicateInReferences;
35
36 private List<TaxonNameBase> duplicateNames;
37
38 private List<TeamOrPersonBase> duplicateCombinationAuthorTeams;
39 private List<TeamOrPersonBase> duplicateExCombinationAuthorTeams;
40 private List<TeamOrPersonBase> duplicateBasionymAuthorTeams;
41 private List<TeamOrPersonBase> duplicateExBasionymAuthorTeams;
42
43 private static NonViralNameParserImpl nonViralNameParser = NonViralNameParserImpl.NewInstance();
44
45 /**
46 * The name that should get parsed
47 */
48 private NonViralName name;
49
50 /**
51 * The widget, the name got entered in
52 */
53 private Control textWidget;
54
55 private boolean doResolveInReferences;
56
57 /**
58 * Creates a new instance
59 *
60 * @param textWidget
61 * @param name
62 */
63 private ParseHandler(Control textWidget, TaxonNameBase name){
64 if(textWidget != null){
65 this.textWidget = textWidget;
66 checkControlHasText();
67 }else{
68 throw new IllegalArgumentException("text widget must not be null");
69 }
70
71 if(name == null){
72 this.name = nonViralNameParser.getNonViralNameInstance("", PreferencesUtil.getPreferredNomenclaturalCode());
73 }else{
74 this.name = (NonViralName) HibernateProxyHelper.deproxy(name);
75 }
76 }
77
78 /**
79 * Factory method to create a new instance of the this class
80 *
81 * @param textWidget The text widget where the NonViralName may be entered into
82 * @param name An initial NonViralName or null when creating a new name
83 * @return An instance of this class
84 */
85 public static ParseHandler NewInstance(Control textWidget, TaxonNameBase name){
86 return new ParseHandler(textWidget, name);
87
88 }
89
90 /**
91 * Parses a given string and returns a <code>TaxonNameBase</code> instance with the
92 * reuslts of the parsing.
93 *
94 * This method should be used to quickly create a new name from a string.
95 * Wherever the string will be parsed again in subsequent editing, an instance
96 * of <code>ParseHandler</code> should be attached to the text widget.
97 *
98 * @param unparsedNameString
99 * @return
100 */
101 public static TaxonNameBase quickParse(String unparsedNameString){
102 TaxonNameBase name = nonViralNameParser.parseReferencedName(unparsedNameString,
103 PreferencesUtil.getPreferredNomenclaturalCode(), null);
104
105 if (name.hasProblem()) {
106 name.setFullTitleCache(unparsedNameString);
107 }
108
109 return name;
110 }
111
112 /**
113 * Creates an empty <code>TaxonNameBase</code> instance with the nomenclatural code
114 * currently set in preferences.
115 *
116 * @return
117 */
118 public static TaxonNameBase createEmptyName(){
119 return nonViralNameParser.getNonViralNameInstance("", PreferencesUtil.getPreferredNomenclaturalCode());
120 }
121
122 /**
123 * Check if the given control has a getText() method
124 */
125 private void checkControlHasText(){
126 Class clazz = textWidget.getClass();
127
128 try {
129 clazz.getDeclaredMethod("getText", null);
130 } catch (SecurityException e) {
131 } catch (NoSuchMethodException e) {
132 throw new IllegalArgumentException("Given composite does not have a getText method");
133 }
134 }
135
136 /**
137 * Parses the string that was entered into the text widget and returns a
138 * NonViralName object that resulted from the parsing process.
139 *
140 * @return The parsed NonViralName object
141 */
142 public NonViralName parse(){
143
144
145 String unparsedNameString = "";
146 try {
147 Method getText;
148 getText = textWidget.getClass().getDeclaredMethod("getText", null);
149 unparsedNameString = (String) getText.invoke(textWidget, null);
150 } catch (Exception e) {
151 // we should never get here
152 logger.error("Error trying to invoke getText method");
153 }
154
155
156 nonViralNameParser.parseReferencedName(name, unparsedNameString,
157 name.getRank(), true);
158
159 if (name.hasProblem()) {
160 name.setFullTitleCache(unparsedNameString);
161 }
162
163 return name;
164 }
165
166 /**
167 * Parses the string that was entered into the text widget and returns a
168 * NonViralName object that resulted from the parsing process.
169 *
170 * The atomized fields (scientific name, author and reference) will be matched
171 * against the database to find possible duplicates. If duplicates were found
172 * the respective parts of the NonViralName will be replaced with the found
173 * objects.
174 *
175 * @return The parsed NonViralName object
176 */
177 public NonViralName parseAndResolveDuplicates(){
178
179 parse();
180
181 findMatches(name);
182
183 resolveDuplicates(name);
184
185 return name;
186 }
187
188
189
190
191 /**
192 * @param name The name to resolve duplicates for.
193 */
194 private void resolveDuplicates(NonViralName name) {
195 resolveDuplicateNames(name);
196
197 resolveAllDuplicateAuthors(name);
198
199 resolveDuplicateReferences(name);
200
201 if(duplicateInReferences != null)
202 resolveDuplicateInReferences(name);
203 }
204
205
206 /**
207 * @param name The name to resolve duplicates for.
208 */
209 private void resolveDuplicateNames(NonViralName name) {
210
211 if (duplicateNames.size() == 1){
212 name = (NonViralName) duplicateNames.iterator().next();
213 }else if(duplicateNames.size() > 1){
214 // FIXME TODO resolve multiple duplications. Use first match for a start
215 name = (NonViralName) duplicateNames.iterator().next();
216 }
217 }
218
219 /**
220 * @param name The name to resolve duplicates for.
221 */
222 private void resolveDuplicateReferences(NonViralName name) {
223 if(duplicateReferences.size() == 1){
224 // exactly one match. We assume that the user wants this reference
225 INomenclaturalReference duplicate = duplicateReferences.iterator().next();
226 name.setNomenclaturalReference(duplicate);
227 }else if(duplicateReferences.size() > 1){
228 // FIXME TODO resolve multiple duplications. Use first match for a start
229 INomenclaturalReference duplicate = duplicateReferences.iterator().next();
230 name.setNomenclaturalReference(duplicate);
231 }
232 }
233
234 /**
235 * @param name The name to resolve duplicates for.
236 */
237 private void resolveDuplicateInReferences(NonViralName name) {
238 ReferenceBase reference = (ReferenceBase) HibernateProxyHelper.deproxy(name.getNomenclaturalReference());
239
240 if(duplicateInReferences.size() > 0){
241 ReferenceBase inReference = (ReferenceBase) duplicateInReferences.iterator().next();
242 reference.setInReference(inReference);
243 logger.warn(reference.generateTitle());
244 // FIXME TODO resolve multiple duplications. We use first match for a start
245 logger.warn(reference.getTitleCache());
246 }
247 }
248
249
250 /**
251 * @param name The name to resolve duplicates for.
252 */
253 private void resolveAllDuplicateAuthors(NonViralName name) {
254
255 if(duplicateCombinationAuthorTeams.size() > 0){
256 name.setCombinationAuthorTeam(duplicateCombinationAuthorTeams.iterator().next());
257 ReferenceBase reference = (ReferenceBase) name.getNomenclaturalReference();
258 if(reference != null){
259 reference.setAuthorTeam(duplicateCombinationAuthorTeams.iterator().next());
260 }
261 // FIXME TODO resolve multiple duplications. We use first match for a start.
262 }
263
264 if(duplicateExCombinationAuthorTeams.size() > 0){
265 name.setExCombinationAuthorTeam(duplicateExCombinationAuthorTeams.iterator().next());
266 // FIXME TODO resolve multiple duplications. We use first match for a start.
267 }
268
269 if(duplicateBasionymAuthorTeams.size() > 0){
270 name.setBasionymAuthorTeam(duplicateBasionymAuthorTeams.iterator().next());
271 // FIXME TODO resolve multiple duplications. We use first match for a start.
272 }
273
274 if(duplicateExBasionymAuthorTeams.size() > 0){
275 name.setExBasionymAuthorTeam(duplicateExBasionymAuthorTeams.iterator().next());
276 // FIXME TODO resolve multiple duplications. We use first match for a start.
277 }
278 }
279
280 /**
281 * Splits a NonViralName into its parts and calls methods to find matches for these
282 * parts in the database.
283 *
284 * @param name The NonViralName to find matches for.
285 */
286 private void findMatches(NonViralName name){
287
288 duplicateNames = findMatchingLatinNames(name);
289
290 duplicateCombinationAuthorTeams = findMatchingAuthors((TeamOrPersonBase) name.getCombinationAuthorTeam());
291 duplicateExCombinationAuthorTeams = findMatchingAuthors((TeamOrPersonBase) name.getExCombinationAuthorTeam());
292 duplicateBasionymAuthorTeams = findMatchingAuthors((TeamOrPersonBase) name.getBasionymAuthorTeam());
293 duplicateExBasionymAuthorTeams = findMatchingAuthors((TeamOrPersonBase) name.getExBasionymAuthorTeam());
294
295 INomenclaturalReference nomenclaturalReference = name.getNomenclaturalReference();
296
297 // check if the reference has an inreference and also check if the inReference already exists
298 if(nomenclaturalReference != null){
299 ReferenceBase inReference = ((ReferenceBase)nomenclaturalReference).getInReference();
300 if(inReference != null){
301 doResolveInReferences = true;
302 duplicateInReferences = findMatchingNomenclaturalReference(inReference);
303 }
304 }
305
306 duplicateReferences = findMatchingNomenclaturalReference(nomenclaturalReference);
307 }
308
309 /**
310 * @param nomenclaturalReference The NomenclaturalReference to find matches for.
311 * @return A <code>List</code> of possibly matching NomenclaturalReference's.
312 */
313 private List<INomenclaturalReference> findMatchingNomenclaturalReference(INomenclaturalReference nomenclaturalReference) {
314 if(nomenclaturalReference == null) return new ArrayList<INomenclaturalReference>();
315 try{
316 return CdmStore.getCommonService().findMatching(nomenclaturalReference, MatchStrategyConfigurator.ReferenceMatchStrategy());
317 }catch (MatchException e) {
318 logger.error("Error finding matching references", e);
319 }
320 return null;
321 }
322
323 /**
324 * @param authorTeam The TeamOrPersonBase to find matches for.
325 * @return A <code>List</code> of possibly matching TeamOrPersonBase's.
326 */
327 private List<TeamOrPersonBase> findMatchingAuthors(TeamOrPersonBase authorTeam) {
328
329 if(authorTeam == null){
330 return new ArrayList<TeamOrPersonBase>();
331 }
332
333 try{
334 return CdmStore.getCommonService().findMatching(authorTeam, MatchStrategyConfigurator.TeamOrPersonMatchStrategy());
335 }catch (MatchException e) {
336 logger.error("Error finding matching authors", e);
337 }
338 return null;
339 }
340
341 /**
342 * @param taxonNameBase The TaxonNameBase to find matches for.
343 * @return A <code>List</code> of possibly matching TaxonNameBase's.
344 */
345 private List<TaxonNameBase> findMatchingLatinNames(TaxonNameBase taxonNameBase) {
346
347 try {
348 return CdmStore.getCommonService().findMatching(taxonNameBase, MatchStrategyConfigurator.NonViralNameMatchStrategy());
349
350 } catch (MatchException e) {
351 logger.error("Error finding matching names", e);
352 }
353 return null;
354 }
355
356 /**
357 * @return The Control this ParseHandler is attached to.
358 */
359 public Control getTextWidget() {
360 return textWidget;
361 }
362
363 /**
364 * @param textWidget The textWidget to set
365 */
366 public void setTextWidget(Control textWidget) {
367 this.textWidget = textWidget;
368 checkControlHasText();
369 }
370 }