UTF8 for Coordinate Conversion
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / strategy / parser / location / CoordinateConverter.java
1 // $Id$
2 /**
3 * Copyright (C) 2009 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 *
10 * This file is an Java adaption from the orginal CoordinateConverter written by Dominik Mikiewicz
11 * @see www.cartomatic.pl
12 * @see http://dev.e-taxonomy.eu/svn/trunk/geo/coordinateConverter/CoordinateConverter.cs
13 * @see http://gis.miiz.waw.pl/webapps/coordinateconverter/
14 */
15 package eu.etaxonomy.cdm.strategy.parser.location;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Comparator;
20 import java.util.List;
21 import java.util.regex.Pattern;
22
23 import org.apache.log4j.Logger;
24
25 /**
26 * @author a.mueller
27 * @date 07.06.2010
28 *
29 */
30 public class CoordinateConverter {
31 @SuppressWarnings("unused")
32 private static final Logger logger = Logger.getLogger(CoordinateConverter.class);
33
34 //Patterns
35 private List<CoordinatePattern> patterns;
36
37
38 private class CoordinatePattern{
39 String description;
40 String pattern;
41 }
42
43
44 private Comparator<CustomHemisphereIndicator> lengthComparator = new Comparator<CustomHemisphereIndicator>(){
45 public int compare(CustomHemisphereIndicator ind1, CustomHemisphereIndicator ind2) {
46 return Integer.valueOf(ind1.getLength()).compareTo(ind2.getLength());
47 }
48 };
49
50 //Class constructor
51 public CoordinateConverter() {
52 //initialise pattern array
53 patterns = new ArrayList<CoordinatePattern>();
54
55 //temp pattern variable
56 CoordinatePattern pattern;
57
58
59 //variations of DD.DDD with white space characters
60 pattern = new CoordinatePattern();
61 pattern.description = "Variation of DD.DDD";
62 pattern.pattern =
63 //+/-/Nn/Ss/Ww/EeDD.DDDD
64 "(^" +
65 "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
66 "((\\d{1,3}(\\.|\\,)?(\\s)*$)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*$))" +
67 ")" +
68 ////DD.DDDDNn/Ss/Ww/Ee
69 "|(^" +
70 "(\\s)*((\\d{1,3}(\\.|\\,)?(\\s)*)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*))" +
71 "(W|w|E|e|N|n|S|s)?(\\s)*$" +
72 ")";
73 patterns.add(pattern);
74
75
76 //Variations of DD(\u00B0|d)MM.MMM' with whitespace characters
77 pattern = new CoordinatePattern();
78 pattern.description = "Variation of DD(\u00B0|d)MM.MMM('|m)";
79 pattern.pattern =
80 "(^" +
81 "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
82 "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)?(\u02B9|'|M|m)?$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*(\u02B9|'|M|m)?(\\s)*$))" +
83 ")" +
84 "|(^" +
85 "(\\s)*((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)?(\u02B9|'|M|m)?)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*(\u02B9|'|M|m)?(\\s)*))" +
86 "(W|w|E|e|N|n|S|s)?(\\s)*$" +
87 ")";
88 patterns.add(pattern);
89
90
91 //Variations of DD\u00B0MM'SS.SSS" with whitespace characters
92 pattern = new CoordinatePattern();
93 pattern.description = "Variation of DD(\u00B0|d)MM(\u02B9|m)SS.SSS(\u02BA|s)";
94 pattern.pattern =
95 //+/-/Nn/Ss/Ww/EeDD\u00B0MM\u02B9SS.SSS
96 "(^" +
97 "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
98 "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)?(\\s)*$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*(\u02BA|\"|''|S|s)?(\\s)*$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*(\u02BA|\"|''|S|s)?(\\s)*$))" +
99 ")" +
100 //DD°MM\u02B9SS.SSSNn/Ss/Ww/Ee
101 "|(^" +
102 "(\\s)*((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)?(\\s)*)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*(\u02BA|\"|''|S|s)?(\\s)*)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*(\u02BA|\"|''|S|s)?(\\s)*))" +
103 "(W|w|E|e|N|n|S|s)?(\\s)*$" +
104 ")";
105 patterns.add(pattern);
106
107
108 //Variations of DD:MM:SS.SSS with whitespace characters
109 pattern = new CoordinatePattern();
110 pattern.description = "Variation of DD:MM:SS.SSS";
111 pattern.pattern =
112 // +/-/Nn/Ss/Ww/EeDD:MM:SS.SSS
113 "(^" +
114 "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
115 "((\\d{1,3}(\\s)*\\:?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*$))" +
116 ")" +
117 //DD:MM:SS.SSSNn/Ss/Ww/Ee
118 "|(^" +
119 "(\\s)*((\\d{1,3}(\\s)*\\:?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*))" +
120 "(W|w|E|e|N|n|S|s)?(\\s)*$" +
121 ")";
122 patterns.add(pattern);
123
124 }
125
126
127 //tests if a string matches one of the defined patterns
128 private int matchPattern(String str){
129 int recognised = -1;
130
131 //match the string against each available patern
132 for (int i = 0; i < patterns.size(); i++){
133
134 CoordinatePattern pattern = patterns.get(i);
135 Pattern regEx = Pattern.compile(pattern.pattern);
136 if (regEx.matcher(str).find()) {
137 recognised = i;
138 break;
139 }
140
141 }
142 return recognised;
143 }
144
145
146 //gets sign of the coordinate (tests for presence of negative sign)
147 private int getSign(String str){
148
149 //This regex checks for the negative hemisphere indicator
150 Pattern regexNegative = Pattern.compile("(-|S|s|W|w)");
151
152 //This regex checks if there weren't any other hemisphere indicators
153 //it is needed for the specific case of the DDdMMmSSs S
154 //so it needs to be ensured there where no positive indicators
155 Pattern regexPositive = Pattern.compile("(\\+|N|n|E|e)");
156
157 //if a positive indicator is found no need to search further
158 if (regexPositive.matcher(str).find()){
159 return 1;
160 }else{
161 //if not check whether there was a negative indicator. if so negate otherwise return positive
162 if (regexNegative.matcher(str).find()){
163 return -1;
164 }else{
165 return 1;
166 }
167 }
168 }
169
170
171 //this checks for the coordinate sign by evaluating user supplied data
172 private int getCustomSign(String str){
173 //Note:
174 //Indicators are evaluated from the longest ones to the shortes ones
175 //So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
176
177
178 //search for the presence of indicators
179 boolean hasPositive = false;
180 boolean hasNegative = false;
181
182 //keep previous negative indicators here
183 List<String> previousNegatives = new ArrayList<String>();
184
185 //compare the string with user supplied custom pattern
186 for (int x = customPtrn.hemisphereIndicators.size() - 1; x >= 0; x--){
187
188 CustomHemisphereIndicator ind = customPtrn.hemisphereIndicators.get(x);
189
190 //test here if the indicator exists (has length >0)
191 if (ind.getLength() > 0){
192
193 //check if the supplied pattern was marked as case insensitive?
194 String caseInsensitive = "";
195
196 if (customPtrn.caseInsensitive){
197 caseInsensitive = "(?i)";
198 }
199
200 //create a regex
201 Pattern tempRegex = Pattern.compile(caseInsensitive + ind.getIndicator());
202
203 //if a pattern is found
204 if (tempRegex.matcher(str).find()){
205 //check whether it's a positive or negative indicator
206 if (ind.getPositive()){
207 /* Note:
208 * See the note below to understand why checking for previous negatives is performed here
209 */
210
211 //check the previous negatives
212 if (previousNegatives.size() != 0){
213 boolean sameNegative = false;
214
215 for (int i = previousNegatives.size() - 1; i >= 0; i--){
216 if (ind.getIndicator() == previousNegatives.get(i)){
217 sameNegative = true;
218 break;
219 }
220 }
221
222 //mark as positive only if the previously found negative is the same
223 if (sameNegative){
224 hasPositive = true;
225 }
226
227 }else{ //if no negatives before it already marks the sign as positive
228 hasPositive = true;
229 }
230
231 } else {
232 /* Note:
233 * save the negative indicator here so it can be compared later if a positive wants to overwrite it!
234 * in a case a longer negative "Pn" has already been found a shorter positive "P" will not overwrite it
235 * and the hasPositive will remain false;
236 * In a case a "P" negative indicator has already been found, positive will mark hasPositive and therefore
237 * later a default positive value will be returned (if the indicators for positive & negative are the same
238 * positive is returned)
239 * testing for previous positives is not required since if a hasPositive is already true method will return
240 * true anyway
241 *
242 */
243 previousNegatives.add(ind.getIndicator());
244
245 hasNegative = true;
246
247 }
248 }
249 }
250
251 }
252
253 //Note:
254 //positive indicator has priority here - if both indicators supplied by the user are the same, a positive is chosen
255 //if there were no indicator found in the tested coordinate, a positive value is returned by default
256
257 if (hasPositive){
258 return 1;
259 } else {
260 if (hasNegative) {
261 return -1;
262 } else {
263 return 1;
264 }
265 }
266 }
267
268
269
270 //returns a currently used decimal separator
271 private String getDecimalSeparator(){
272 //TODO not yet transformed from C#
273 // return System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
274 return ".";
275 }
276
277
278 //replaces comma or dot for current decimal separator
279 private String fixDecimalSeparator(String str){
280 //Note:
281 //Coma is replaced as parsers often recognise dot as a decimal separator
282 //Comma or dot is replaced with a decimal separator here (environment settings)
283 //But decimal separator has to be used later too;
284
285 String regExReplaceComma = "(\\,|\\.)";
286 str = str.replaceAll(regExReplaceComma, getDecimalSeparator());
287
288 return str;
289 }
290
291
292 //removes sign
293 private String removeSign(String str){
294 String regExRemoveSign = "(\\+|-|S|s|W|w|N|n|E|e)";
295 str = str.replaceAll(regExRemoveSign, "");
296 return str;
297 }
298
299 //removes custom sign indicators
300 private String removeCustomPatternParts(String str){
301
302 /* Note:
303 * Symbols are added here so the removing tries to not affect the coordinate too much
304 * Strings to be removed then are evaluated from the longest ones to the shortes ones
305 * So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
306 * */
307
308 //CustomHemisphereIndicator is used here so another object does not have to be created
309 //only for the string cleanning
310 List<CustomHemisphereIndicator> stringsToRemove = customPtrn.hemisphereIndicators;
311
312 //add degree symbol
313 CustomHemisphereIndicator stringToRemove = new CustomHemisphereIndicator("Degree", customPtrn.degreeSymbol,customPtrn.degreeSymbol.length(), false);
314 stringsToRemove.add(stringToRemove);
315
316 //add minute symbol
317 stringToRemove = new CustomHemisphereIndicator("Minute", customPtrn.minuteSymbol, customPtrn.minuteSymbol.length(), false);
318 stringsToRemove.add(stringToRemove);
319
320 //add second symbol
321 stringToRemove = new CustomHemisphereIndicator("Second", customPtrn.secondSymbol, customPtrn.secondSymbol.length(), false);
322 stringsToRemove.add(stringToRemove);
323
324 //sort the list (by element's Length property)
325 Collections.sort(stringsToRemove, lengthComparator);
326
327
328 // ListSelectionEv.sort(lengthComparator);
329
330
331 for (int x = stringsToRemove.size() - 1; x >= 0; x--){
332
333 CustomHemisphereIndicator toBeRemoved = stringsToRemove.get(x);
334
335 //check if the string exists so replacing does not yield errors
336 if (toBeRemoved.getLength() > 0)
337 {
338 //check if the supplied pattern was marked as case insensitive?
339 String CaseInsensitive = "";
340
341 if (customPtrn.caseInsensitive){
342 CaseInsensitive = "(?i)";
343 }
344
345 //create regex for replacing
346 String tempRegex = CaseInsensitive + toBeRemoved.getIndicator();
347
348
349 if (toBeRemoved.getName().equals("Degree") || toBeRemoved.getName().equals("Minute")) {
350 //replace with a symbol used later for splitting
351 str = str.replaceAll(tempRegex, ":");
352 } else {
353 //remove the string
354 str = str.replaceAll(tempRegex, "");
355 }
356 }
357 }
358 return str;
359 }
360
361
362
363 //removes whitespace characters
364 private String removeWhiteSpace(String str){
365 str = str.replaceFirst("\\s*", "");
366 return str;
367 }
368
369
370 //Object for the conversion results
371 public class ConversionResults{
372 public boolean patternRecognised;
373 public String patternMatched;
374 public String patternType;
375
376 public boolean conversionSuccessful;
377 public double convertedCoord;
378 public boolean canBeLat;
379
380 public String conversionComments;
381
382 public Boolean isLongitude;
383
384 public int dd;
385 public int mm;
386 public double mmm;
387 public int ss;
388 public double sss;
389
390 }
391
392
393 public ConversionResults tryConvert(String str){
394 //some local variables
395 int sign; //sign of the coordinate
396 String[] decimalBit, ddmmss, ddmm; //arrays for splitting
397 double dd = 0, mm = 0, ss = 0, mmm = 0, sss = 0, dec = 0; //parts of the coordinates
398
399 String decSeparatorRaw = String.valueOf(getDecimalSeparator()); //gets the current decimal separator
400 String decSeparatorRegEx = decSeparatorRaw.replace(".", "\\.");
401
402 ConversionResults results = new ConversionResults();
403
404 //Get the matched pattern
405 CoordinatePattern pattern;
406 int ptrnnum = matchPattern(str);
407 if (ptrnnum != -1) {
408 pattern = patterns.get(ptrnnum);
409 } else {
410 pattern = new CoordinatePattern();
411 pattern.description = "Unknown";
412 pattern.pattern = "No pattern matched";
413 }
414
415
416
417 if (pattern.description.equals("Variation of DD.DDD")){
418
419 //Sets pattern machted, successful, pattern type and pattern info
420 initializeResult(results, pattern);
421
422 //get sign
423 sign = getSign(str);
424 results.isLongitude = getIsLongitude(str);
425
426 //Replace comma or dot with a current decimal separator
427 str = fixDecimalSeparator(str);
428
429 //Remove all the unwanted stuff
430 str = removeSign(str);
431 str = removeWhiteSpace(str);
432
433 //Since this is already a decimal degree no spliting is needed
434 dd = Double.valueOf(str);
435
436 checkDegreeRange(dd, results);
437 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
438
439 }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM.MMM('|m)")){
440
441 //Sets pattern machted, successful, pattern type and pattern info
442 initializeResult(results, pattern);
443
444 //get sign
445 sign = getSign(str);
446 results.isLongitude = getIsLongitude(str);
447
448 //Replace comma or dot with a current decimal separator
449 str = fixDecimalSeparator(str);
450
451 //Remove all the unwanted stuff
452 str = removeSign(str);
453 str = removeWhiteSpace(str);
454
455 //do some further replacing
456 //Replace degree symbol
457 str = str.replaceAll("(\u00B0|\u00BA|D|d)", ":");
458
459 //remove minute symbol
460 str = str.replaceAll("(\u02B9|'|M|m)", "");
461
462 //Extract decimal part
463 decimalBit = str.split(decSeparatorRegEx);
464
465 //split degrees and minutes
466 ddmm = decimalBit[0].split(":");
467
468
469 //extract values from the strings
470 dd = Integer.valueOf(ddmm[0]); //Degrees
471
472 if (ddmm.length > 1){ //Minutes
473 //check if the string is not empty
474 if (ddmm[1] != "") {
475 mm = Integer.valueOf(ddmm[1]);
476 }
477 }
478
479 if (decimalBit.length > 1){//DecimalSeconds
480 //check if the string is not empty
481 if (decimalBit[1] != "") {
482 mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
483 }
484 }
485
486 checkDegreeRange(dd, results);
487 checkMinuteRange(mm, results);
488 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
489
490 }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM(\u02B9|m)SS.SSS(\u02BA|s)")){
491
492 /*
493 * Note:
494 * This pattern allows the seconds to be specified with S, s or " or nothing at all
495 * If the seconds are marked with "s" and there is no other indication of the hemisphere
496 * the coordinate will be parsed as southern (negative).
497 *
498 * If the N / E / W / + indicator is found the coordinate will be parsed appropriately no matter
499 * what is the second notation
500 */
501
502 //Sets pattern machted, successful, pattern type and pattern info
503 initializeResult(results, pattern);
504
505 //get sign
506 sign = getSign(str);
507 //TODO test S
508 results.isLongitude = getIsLongitude(str);
509
510 //Replace comma or dot with a current decimal separator
511 str = fixDecimalSeparator(str);
512
513 //Remove all the unwanted stuff
514 str = removeSign(str);
515 str = removeWhiteSpace(str);
516
517 //remove second symbol (s is removed by the get sign method)
518 //double apostrophe is not removed here as single apostrphe may mark minutes!
519 //it's taken care of later after extracting the decimal part
520 str = str.replaceAll("(u\02BA|\")", "");
521
522 //do some further replacing
523 //Replace degree symbol
524 str = str.replaceAll("(\u00B0|\u00B0|D|d|\u02B9|'|M|m)",":");
525
526 //Extract decimal part
527 decimalBit = str.split(decSeparatorRegEx);
528
529 //remove : from the decimal part [1]! This is needed when a double apostrophe was used to mark seconds
530 if (decimalBit.length > 1)
531 {
532 decimalBit[1].replace(":", "");
533 }
534
535 //split degrees and minutes
536 ddmmss = decimalBit[0].split(":");
537
538
539 //extract values from the strings
540 dd = Integer.valueOf(ddmmss[0]); //Degrees
541 if (ddmmss.length > 1){//Minutes
542 //check if the string is not empty
543 if (ddmmss[1] != "") {
544 mm = Integer.valueOf(ddmmss[1]);
545 }
546 }
547 if (ddmmss.length > 2){//Seconds
548 //check if the string is not empty
549 if (ddmmss[2] != "") {
550 ss = Integer.valueOf(ddmmss[2]);
551 }
552 }
553 if (decimalBit.length > 1) { //DecimalSeconds
554 //check if the string is not empty
555 if (decimalBit[1] != "") {
556 sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
557 }
558 }
559
560 checkDegreeRange(dd, results);
561 checkMinuteRange(mm, results);
562 checkSecondRange(ss, results);
563
564 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
565
566 }else if (pattern.description.equals("Variation of DD:MM:SS.SSS")){
567
568 //Sets pattern machted, successful, pattern type and pattern info
569 initializeResult(results, pattern);
570
571 //get sign
572 sign = getSign(str);
573 results.isLongitude = getIsLongitude(str);
574
575 //Replace comma or dot with a current decimal separator
576 str = fixDecimalSeparator(str);
577
578 //Remove all the unwanted stuff
579 str = removeSign(str);
580 str = removeWhiteSpace(str);
581
582 //Do some splitting
583 decimalBit = str.split(decSeparatorRegEx);
584 ddmmss = decimalBit[0].split(":");
585
586
587 //extract values from the strings
588 dd = Integer.valueOf(ddmmss[0]); //Degrees
589 if (ddmmss.length > 1)//Minutes
590 {
591 //check if the string is not empty
592 if (ddmmss[1] != "") { mm = Integer.valueOf(ddmmss[1]); }
593 }
594 if (ddmmss.length > 2) {//Seconds{
595 //check if the string is not empty
596 if (ddmmss[2] != "") {
597 ss = Integer.valueOf(ddmmss[2]);
598 }
599 }
600 if (decimalBit.length > 1) { //DecimalSeconds
601 //check if the string is not empty
602 if (decimalBit[1] != "") {
603 sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
604 }
605 }
606
607 checkDegreeRange(dd, results);
608 checkMinuteRange(mm, results);
609 checkSecondRange(ss, results);
610
611 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
612
613 }else if (pattern.description.equals("Custom variation of DD.DDD")){
614
615 //Sets pattern machted, successful, pattern type and pattern info
616 initializeResult(results, pattern);
617
618
619 //get sign
620 sign = getCustomSign(str);
621
622 //TODO still needs to be adapted to custom pattern
623 results.isLongitude = getIsLongitude(str);
624
625
626 //Remove all the unwanted stuff
627 //Note: This method also replaces the symbols with ":"
628 //Note: In certain cases it may make the coord unparsable
629 str = removeCustomPatternParts(str);
630
631 str = removeWhiteSpace(str);
632
633 //Replace comma or dot with a current decimal separator
634 str = fixDecimalSeparator(str);
635
636 //remove the ":" here as it is not needed here for decimal degrees
637 str = str.replace(":", "");
638
639 try {
640 //Since this is already a decimal degree no spliting is needed
641 dd = Double.valueOf(str);
642 } catch (Exception e) {
643 results.conversionSuccessful = false;
644 results.convertedCoord = 99999; //this is to mark an error...
645 results.conversionComments =
646 "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
647 "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
648 "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
649 ;
650
651 //exit method
652 return results;
653 }
654
655 //Since this is already a decimal degree no spliting is needed
656 dd = Double.valueOf(str);
657
658 checkDegreeRange(dd, results);
659 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
660
661
662 }else if (pattern.description.equals("Custom variation of DD:MM.MMM")){
663 //-------------Customs patterns start here-------------
664
665 //Sets pattern machted, successful, pattern type and pattern info
666 initializeResult(results, pattern);
667
668 //get sign
669 sign = getCustomSign(str);
670
671 //TODO still needs to be adapted to custom pattern
672 results.isLongitude = getIsLongitude(str);
673
674
675
676 //Remove all the unwanted stuff
677 //Note: This method also replaces the symbols with ":"
678 //Note: In certain cases it may make the coord unparsable
679 str = removeCustomPatternParts(str);
680
681 str = removeWhiteSpace(str);
682
683 //Replace comma or dot with a current decimal separator
684 str = fixDecimalSeparator(str);
685
686
687 //Extract decimal part
688 decimalBit = str.split(decSeparatorRegEx);
689
690 //split degrees and minutes
691 ddmm = decimalBit[0].split(":");
692
693
694 try {
695 //extract values from the strings
696 dd = Integer.valueOf(ddmm[0]); //Degrees
697
698 if (ddmm.length > 1){//Minutes
699 //check if the string is not empty
700 if (ddmm[1] != "") { mm = Integer.valueOf(ddmm[1]); }
701 }
702
703 if (decimalBit.length > 1){//DecimalSeconds
704 //check if the string is not empty
705 if (decimalBit[1] != ""){
706 //replace the ":" if any (may be here as a result of custom symbol replacement
707 decimalBit[1] = decimalBit[1].replace(":", "");
708
709 mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
710 }
711 }
712 } catch (Exception e){
713 results.conversionSuccessful = false;
714 results.convertedCoord = 99999; //this is to mark an error...
715 results.conversionComments =
716 "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
717 "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
718 "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
719 ;
720
721 //exit method
722 return results;
723 }
724
725
726 checkDegreeRange(dd, results);
727 checkMinuteRange(mm, results);
728 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
729
730 } else if (pattern.description.equals("Custom variation of DD:MM:SS.SSS")){
731
732 //Sets pattern machted, successful, pattern type and pattern info
733 initializeResult(results, pattern);
734
735
736 //get sign
737 sign = getCustomSign(str);
738
739 //TODO still needs to be adapted to custom pattern
740 results.isLongitude = getIsLongitude(str);
741
742
743 //Remove all the unwanted stuff
744 //Note: This method also replaces the symbols with ":"
745 //Note: In certain cases it may make the coord unparsable
746 str = removeCustomPatternParts(str);
747
748 str = removeWhiteSpace(str);
749
750 //Replace comma or dot with a current decimal separator
751 str = fixDecimalSeparator(str);
752
753
754 //Extract decimal part
755 decimalBit = str.split(decSeparatorRegEx);
756
757 //split degrees and minutes
758 ddmmss = decimalBit[0].split(":");
759
760
761 try {
762
763 //extract values from the strings
764 dd = Integer.valueOf(ddmmss[0]); //Degrees
765 if (ddmmss.length > 1) {//Minutes
766 //check if the string is not empty
767 if (ddmmss[1] != "") {
768 mm = Integer.valueOf(ddmmss[1]);
769 }
770 }
771 if (ddmmss.length > 2){ //Seconds
772 //check if the string is not empty
773 if (ddmmss[2] != "") {
774 ss = Integer.valueOf(ddmmss[2]);
775 }
776 }
777 if (decimalBit.length > 1){ //DecimalSeconds
778 //check if the string is not empty
779 if (decimalBit[1] != "") {
780 sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
781 }
782 }
783 } catch (Exception e) {
784 results.conversionSuccessful = false;
785 results.convertedCoord = 99999; //this is to mark an error...
786 results.conversionComments =
787 "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
788 "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
789 "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
790 ;
791
792 //exit method
793 return results;
794 }
795
796
797 checkDegreeRange(dd, results);
798 checkMinuteRange(mm, results);
799 checkSecondRange(ss, results);
800
801 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
802
803 }else { //default : pattern not recognized
804 results.patternRecognised = false;
805 results.patternType = pattern.description;
806 results.patternMatched = pattern.pattern;
807
808 results.conversionSuccessful = false;
809 results.convertedCoord = 99999; //this is to mark an error...
810
811 results.conversionComments = "Coordinate pattern not recognised!";
812
813 }
814
815 //do the self check here
816 results = selfTest(results);
817
818 //return conversion results
819 return results;
820 }
821
822
823 /**
824 * @param sign
825 * @param dd
826 * @param mm
827 * @param ss
828 * @param sss
829 * @param results
830 */
831 private void doConvertWithCheck(int sign, double dd, double mm, double mmm, double ss, double sss, ConversionResults results) {
832 double dec;
833 //Do the conversion if everything ok
834 if (results.conversionSuccessful){
835 results.conversionComments = "Conversion successful.";
836
837 dec = sign * (dd + (mm + mmm) / 60 + (ss + sss) / 3600);
838
839 //one more check to ensure a coord does not exceed 180
840 if (dec > 180 | dec < -180){
841 results.conversionSuccessful = false;
842 results.convertedCoord = 99999; //this is to mark an error...
843 results.conversionComments += "Coordinate is either > 180 or < -180; ";
844 } else {
845 results.convertedCoord = dec;
846
847 results.conversionComments = "Conversion successful.";
848
849 //Check whether the coordinate exceeds +/- 90 and mark it in comments
850
851 if (dec <= 90 && dec >= -90 && (results.isLongitude == null || results.isLongitude == false) ) {
852 results.canBeLat = true;
853 }else{
854 results.isLongitude = true;
855 }
856 }
857 }
858 }
859
860
861 /**
862 * @param ss
863 * @param results
864 */
865 private void checkSecondRange(double ss, ConversionResults results) {
866 if (ss > 59) {//seconds
867 results.conversionSuccessful = false;
868 results.convertedCoord = 99999; //this is to mark an error...
869 results.conversionComments += "Seconds fall outside the range: MM >= 60; ";
870 }
871 }
872
873
874 /**
875 * @param mm
876 * @param results
877 */
878 private void checkMinuteRange(double mm, ConversionResults results) {
879 if (mm > 59) {//minutes
880 results.conversionSuccessful = false;
881 results.convertedCoord = 99999; //this is to mark an error...
882 results.conversionComments += "Minutes fall outside the range: MM > 59; ";
883 }
884 }
885
886
887 /**
888 * @param dd
889 * @param results
890 */
891 private void checkDegreeRange(double dd, ConversionResults results) {
892 //do some additional checking if the coords fall into the range
893 if (dd < -180 | dd > 180){ //degree may require another param specifying whether it's lat or lon...
894 results.conversionSuccessful = false;
895 results.convertedCoord = 99999; //this is to mark an error...
896 results.conversionComments += "Degrees fall outside the range: DD < -180 | DD > 180; ";
897 }
898 }
899
900
901 /**
902 * @param str
903 * @return
904 */
905 private Boolean getIsLongitude(String str) {
906 //This regex checks for the negative hemisphere indicator
907 Pattern regexLatitudeNonAmbigous = Pattern.compile("(N|n)");
908 Pattern regexLatitudeAmbigous = Pattern.compile("(S|s)");
909
910 //This regex checks if there weren't any other hemisphere indicators
911 //it is needed for the specific case of the DDdMMmSSs S
912 //so it needs to be ensured there where no positive indicators
913 Pattern regexLongitude = Pattern.compile("(W|w|E|e)");
914
915 //if a positive indicator is found no need to search further
916 if (regexLongitude.matcher(str).find()){
917 return true;
918 }else if (regexLatitudeNonAmbigous.matcher(str).find()){
919 return false;
920 }else if (regexLatitudeAmbigous.matcher(str).find()){
921 Pattern regexLiteralUnits = Pattern.compile("(D|d|M|m)");
922
923 //if there are no other literal units we assume that S is a
924 //direction and not a second indicator
925 if (! regexLiteralUnits.matcher(str).find()){
926 return false;
927 }else if (regexLatitudeAmbigous.matcher(str).groupCount() > 1){
928 return false;
929 }else{
930 return null;
931 }
932 }else{
933 return null;
934 }
935 }
936
937
938 /**
939 * Sets pattern machted, successful, pattern type and pattern info
940 * @param results
941 * @param pattern
942 */
943 private void initializeResult(ConversionResults results,
944 CoordinatePattern pattern) {
945 //Pattern matched
946 results.patternRecognised = true;
947
948 //Matching pattern succeeded so intialy the parsing is ok
949 results.conversionSuccessful = true;
950
951 //pattern info
952 results.patternType = pattern.description;
953 results.patternMatched = pattern.pattern;
954 }
955
956
957 private ConversionResults selfTest(ConversionResults results){
958
959 ConversionResults newresults = results;
960
961 if (results.conversionSuccessful != false){
962 int sign = 1;
963 if (Math.signum(results.convertedCoord) < 0) {
964 sign = -1;
965 }
966
967 double decimalDegrees = sign * results.convertedCoord;
968 int fullDegrees;
969
970 double decimalMinutes;
971 int fullMinutes;
972
973 double decimalSeconds;
974 int fullSeconds;
975
976 //Get full degrees
977 fullDegrees = (int)Math.floor(decimalDegrees);
978
979 //get minutes
980 decimalMinutes = (decimalDegrees - fullDegrees) * 60;
981 fullMinutes = (int)Math.floor(decimalMinutes);
982
983 decimalSeconds = (decimalMinutes - fullMinutes) * 60;
984 fullSeconds = (int)Math.floor(decimalSeconds);
985
986 //save the test results
987 newresults.dd = fullDegrees;
988 newresults.mm = fullMinutes;
989 newresults.mmm = decimalSeconds;
990 newresults.ss = fullSeconds;
991 newresults.sss = decimalSeconds;
992
993 }
994
995 return newresults;
996
997 }
998
999
1000
1001 //------------ CUSTOM PATTERN BUILDER--------------
1002
1003 public class CustomPatternIn {
1004 public String north;
1005 public String south;
1006 public String east;
1007 public String west;
1008
1009 public String degreeSymbol;
1010 public String minuteSymbol;
1011 public String secondSymbol;
1012
1013 public boolean caseInsensitive;
1014 public boolean allowWhiteSpace;
1015 public boolean priorityOverDefaultPatterns;
1016 public boolean disableDefaultPatterns;
1017
1018 }
1019
1020
1021 private class CustomPattern{
1022
1023 public List<CustomHemisphereIndicator> hemisphereIndicators;
1024
1025 public String degreeSymbol;
1026 public String minuteSymbol;
1027 public String secondSymbol;
1028
1029 public boolean caseInsensitive;
1030
1031 }
1032
1033 //global variable to be used if a custom pattern is used
1034 private CustomPattern customPtrn;
1035
1036 //escape some of the chars
1037 private String escapeChars(String str){
1038 // backslash - first so it is not messed when other escape chars are corrected for being used in a string
1039 str = str.replace("\\", "\\\\");
1040
1041 //dot and comma
1042 str = str.replace(".", "\\.");
1043 str = str.replace(",", "\\,");
1044
1045 //brackets
1046 str = str.replace("(", "\\(");
1047 str = str.replace(")", "\\)");
1048 str = str.replace("[", "\\[");
1049 str = str.replace("]", "\\]");
1050 str = str.replace("{", "\\{");
1051 str = str.replace("}", "\\}");
1052
1053 //other replacements
1054 str = str.replace("^", "\\^");
1055 str = str.replace("$", "\\$");
1056 str = str.replace("+", "\\+");
1057 str = str.replace("*", "\\*");
1058 str = str.replace("?", "\\?");
1059 str = str.replace("|", "\\|");
1060
1061 return str;
1062 }
1063
1064
1065 //this implements sorting by using system.Icomparable - sorting is needed later when replacing
1066 private class CustomHemisphereIndicator implements Comparable<CustomHemisphereIndicator> {
1067 //private variables
1068 private int m_length;
1069 private String m_name;
1070 private String m_indicator;
1071 private boolean m_positive;
1072
1073 //constructor
1074 public CustomHemisphereIndicator(String name, String indicator, int length, boolean positive){
1075 this.m_name = name;
1076 this.m_indicator = indicator;
1077 this.m_length = length;
1078 this.m_positive = positive;
1079 }
1080
1081 //properties
1082
1083 public String getName(){
1084 return this.m_name;
1085 }
1086 public void setName(String value){
1087 this.m_name = value;
1088 }
1089
1090 public String getIndicator(){
1091 return this.m_indicator;
1092 }
1093 public void setIndicator(String value){
1094 this.m_indicator = value;
1095 }
1096
1097 public int getLength(){
1098 return this.m_length;
1099 }
1100 public void setLength(int value){
1101 this.m_length = value;
1102 }
1103
1104
1105 public boolean getPositive(){
1106 return this.m_positive;
1107 }
1108 public void setPositive(boolean value){
1109 this.m_positive = value;
1110 }
1111
1112 /* Less than zero if this instance is less than obj.
1113 * Zero if this instance is equal to obj.
1114 * Greater than zero if this instance is greater than obj.
1115 *
1116 * This method uses the predefined method Int32.CompareTo
1117 * */
1118
1119 public int compareTo(CustomHemisphereIndicator ind){
1120
1121 //no need to rewrite the code again, we have Integer.compareTo ready to use
1122 return Integer.valueOf(this.getLength()).compareTo(Integer.valueOf(ind.getLength()));
1123 }
1124 }
1125
1126
1127
1128 //This adds custom pattern to a list of already predefined patterns
1129 //useful for batch conversions - allows for totally mixed input data (predefined & custom)
1130 public void addCustomPattern(CustomPatternIn patternIn){
1131
1132 //new custom pattern object - to pass the needed data farther
1133 CustomPattern pattern = new CustomPattern();
1134
1135 //keep indicators for parsing
1136 List<CustomHemisphereIndicator> indicators = new ArrayList<CustomHemisphereIndicator>();
1137
1138 //north
1139 CustomHemisphereIndicator ind = new CustomHemisphereIndicator("North", patternIn.north, patternIn.north.length() ,true);
1140 indicators.add(ind);
1141
1142 //south
1143 ind = new CustomHemisphereIndicator("South", patternIn.south, patternIn.south.length(), false);
1144 indicators.add(ind);
1145
1146 //east
1147 ind = new CustomHemisphereIndicator("East", patternIn.east, patternIn.east.length(), true);
1148 indicators.add(ind);
1149
1150 //west
1151 ind = new CustomHemisphereIndicator("West", patternIn.west, patternIn.west.length(), false);
1152 indicators.add(ind);
1153
1154 //sort the arraylist
1155 Collections.sort(indicators, lengthComparator);
1156
1157
1158 //add it to the pattern object
1159 pattern.hemisphereIndicators = indicators;
1160
1161 //case insensitive
1162 pattern.caseInsensitive = patternIn.caseInsensitive;
1163
1164 //keep symbols for parsing
1165 pattern.degreeSymbol = patternIn.degreeSymbol;
1166 pattern.minuteSymbol = patternIn.minuteSymbol;
1167 pattern.secondSymbol = patternIn.secondSymbol;
1168
1169
1170 //save the data
1171 customPtrn = pattern;
1172
1173
1174 //----------------build custom patterns----------------
1175
1176 //prepare hemisphere indicators
1177 String north = escapeChars(patternIn.north);
1178 String south = escapeChars(patternIn.south);
1179 String east = escapeChars(patternIn.east);
1180 String west = escapeChars(patternIn.west);
1181
1182 //prepare symbols
1183 String degreesymbol = "";
1184 if (patternIn.degreeSymbol != ""){
1185 degreesymbol = "(" + escapeChars(patternIn.degreeSymbol) + ")?";
1186 }
1187
1188 String minutesymbol = "";
1189 if (patternIn.minuteSymbol != ""){
1190 minutesymbol = "(" + escapeChars(patternIn.minuteSymbol) + ")?";
1191 }
1192
1193 String secondsymbol = "";
1194 if (escapeChars(patternIn.secondSymbol) != ""){
1195 secondsymbol = "(" + escapeChars(patternIn.secondSymbol) + ")?";
1196 }
1197
1198
1199 //is the pattern to be case insensitive?
1200 String CaseInsensitive = "";
1201 if (patternIn.caseInsensitive){
1202 CaseInsensitive = "(?i)";
1203 }
1204
1205 //allow whitespace
1206 String WhiteSpace = "";
1207 if (patternIn.allowWhiteSpace == true){
1208 WhiteSpace = "(\\s)*";
1209 }
1210
1211 //hemisphere indicator
1212 String HemisphereIndicator = "";
1213
1214 //add north if present
1215 if (north == ""){
1216 HemisphereIndicator += south;
1217 }else{
1218 HemisphereIndicator += north;
1219 if (south != ""){
1220 HemisphereIndicator += "|" + south;
1221 }
1222 }
1223
1224 //add east
1225 if (north == "" & south == ""){
1226 HemisphereIndicator += east;
1227 } else {
1228 if (east != ""){
1229 HemisphereIndicator += "|" + east;
1230 }
1231 }
1232
1233 //add west
1234 if (north == "" & south == "" & east == ""){
1235 HemisphereIndicator += west;
1236 } else {
1237 if (west != "") {
1238 HemisphereIndicator += "|" + west;
1239 }
1240 }
1241
1242 //add remaining bits if not empty
1243 if (HemisphereIndicator != "") {
1244 HemisphereIndicator = "(" + HemisphereIndicator + ")?";
1245 }
1246
1247 List<CoordinatePattern> customPatterns = new ArrayList<CoordinatePattern>();
1248
1249 //create custom patterns based on the specified user's input
1250 CoordinatePattern ptrn;
1251
1252 //Custom variation of DD.DDD
1253 ptrn = new CoordinatePattern();
1254 ptrn.description = "Custom variation of DD.DDD";
1255 ptrn.pattern =
1256 CaseInsensitive + "(^" +
1257 WhiteSpace + HemisphereIndicator + WhiteSpace +
1258 "(" +
1259 "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + "$)" +
1260 ")" +
1261 "|(^" + WhiteSpace +
1262 "(" +
1263 "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + ")" +
1264 ")" +
1265 HemisphereIndicator + WhiteSpace + "$" +
1266 "))"
1267 ;
1268 customPatterns.add(ptrn);
1269
1270 //Custom variation of DD:MM.MMM
1271 ptrn = new CoordinatePattern();
1272 ptrn.description = "Custom variation of DD:MM.MMM";
1273 ptrn.pattern =
1274 CaseInsensitive + "(^" +
1275 WhiteSpace + HemisphereIndicator + WhiteSpace +
1276 "(" +
1277 "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + minutesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + minutesymbol + WhiteSpace + "$)" +
1278 ")" +
1279 "|(^" + WhiteSpace +
1280 "(" +
1281 "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + minutesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + minutesymbol + WhiteSpace + ")" +
1282 ")" +
1283 HemisphereIndicator + WhiteSpace + "$" +
1284 "))"
1285 ;
1286 customPatterns.add(ptrn);
1287
1288 //Custom variation of DD:MM:SS.SSS
1289 ptrn = new CoordinatePattern();
1290 ptrn.description = "Custom variation of DD:MM:SS.SSS";
1291 ptrn.pattern =
1292 CaseInsensitive + "(^" +
1293 WhiteSpace + HemisphereIndicator + WhiteSpace +
1294 "(" +
1295 "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + secondsymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + secondsymbol + WhiteSpace + "$)" +
1296 ")" +
1297 "|(^" + WhiteSpace +
1298 "(" +
1299 "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + secondsymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + secondsymbol + WhiteSpace + ")" +
1300 ")" +
1301 HemisphereIndicator + WhiteSpace + "$" +
1302 "))"
1303 ;
1304 customPatterns.add(ptrn);
1305
1306 //check if the default patterns are to be used
1307 if (patternIn.disableDefaultPatterns) {
1308 patterns = customPatterns;
1309 } else { //if all patterns are to be used check which set has the matching priority
1310
1311 //check if the custom patterns are to have priority over the default ones
1312 if (patternIn.priorityOverDefaultPatterns){
1313
1314 //add default patterns to the custom patterns
1315 for (int i = 0; i < patterns.size(); i++){
1316 customPatterns.add(patterns.get(i));
1317 }
1318
1319 //swap array lists
1320 patterns = customPatterns;
1321
1322 }else{
1323 //add custom patterns to the default patterns
1324 for (int i = 0; i < customPatterns.size(); i++){
1325 patterns.add(customPatterns.get(i));
1326
1327 }
1328 }
1329 }
1330 }
1331
1332 }