View Javadoc
1   package org.kuali.ole.utility.callnumber;
2   
3   /**
4    * Created with IntelliJ IDEA.
5    * User: ?
6    * Date: 19/2/13
7    * Time: 9:23 PM
8    * To change this template use File | Settings | File Templates.
9    */
10  
11  import org.marc4j.marc.DataField;
12  import org.marc4j.marc.Record;
13  import org.marc4j.marc.Subfield;
14  import org.marc4j.marc.VariableField;
15  import org.slf4j.Logger;
16  import org.slf4j.LoggerFactory;
17  
18  import java.io.*;
19  import java.net.MalformedURLException;
20  import java.net.URL;
21  import java.text.DecimalFormat;
22  import java.text.SimpleDateFormat;
23  import java.util.*;
24  import java.util.regex.Matcher;
25  import java.util.regex.Pattern;
26  
27  /**
28   * General utility functions for solrmarc
29   *
30   * @author Wayne Graham
31   * @version $Id: Utils.java 1612 2012-03-21 17:39:16Z rh9ec@virginia.edu $
32   */
33  
34  public final class Utils {
35  
36      private final static Pattern FOUR_DIGIT_PATTERN_BRACES = Pattern.compile("\\[[12]\\d{3,3}\\]");
37      private final static Pattern FOUR_DIGIT_PATTERN_ONE_BRACE = Pattern.compile("\\[[12]\\d{3,3}");
38      private final static Pattern FOUR_DIGIT_PATTERN_STARTING_WITH_1_2 = Pattern.compile("(20|19|18|17|16|15)[0-9][0-9]");
39      private final static Pattern FOUR_DIGIT_PATTERN_OTHER_1 = Pattern.compile("l\\d{3,3}");
40      private final static Pattern FOUR_DIGIT_PATTERN_OTHER_2 = Pattern.compile("\\[19\\]\\d{2,2}");
41      private final static Pattern FOUR_DIGIT_PATTERN_OTHER_3 = Pattern.compile("(20|19|18|17|16|15)[0-9][-?0-9]");
42      private final static Pattern FOUR_DIGIT_PATTERN_OTHER_4 = Pattern.compile("i.e. (20|19|18|17|16|15)[0-9][0-9]");
43      private final static Pattern BC_DATE_PATTERN = Pattern.compile("[0-9]+ [Bb][.]?[Cc][.]?");
44      private final static Pattern FOUR_DIGIT_PATTERN = Pattern.compile("\\d{4,4}");
45      private static Matcher matcher;
46      private static Matcher matcher_braces;
47      private static Matcher matcher_one_brace;
48      private static Matcher matcher_start_with_1_2;
49      private static Matcher matcher_l_plus_three_digits;
50      private static Matcher matcher_bracket_19_plus_two_digits;
51      private static Matcher matcher_ie_date;
52      private static Matcher matcher_bc_date;
53      private static Matcher matcher_three_digits_plus_unk;
54      private final static DecimalFormat timeFormat = new DecimalFormat("00.00");
55      protected static Logger logger = LoggerFactory.getLogger(Utils.class);
56  
57      /**
58       * Default Constructor
59       * It's private, so it can't be instantiated by other objects
60       */
61      private Utils() {
62      }
63  
64      /**
65       * Check first for a particular property in the System Properties, so that the -Dprop="value" command line arg
66       * mechanism can be used to override values defined in the passed in property file.  This is especially useful
67       * for defining the marc.source property to define which file to operate on, in a shell script loop.
68       *
69       * @param props    - property set in which to look.
70       * @param propname - name of the property to lookup.
71       * @returns String - value stored for that property (or null if it doesn't exist)
72       */
73      public static String getProperty(Properties props, String propname) {
74          return getProperty(props, propname, null);
75      }
76  
77      /**
78       * Check first for a particular property in the System Properties, so that the -Dprop="value" command line arg
79       * mechanism can be used to override values defined in the passed in property file.  This is especially useful
80       * for defining the marc.source property to define which file to operate on, in a shell script loop.
81       *
82       * @param props    - property set in which to look.
83       * @param propname - name of the property to lookup.
84       * @param defVal   - the default value to use if property is not defined
85       * @returns String - value stored for that property (or the  if it doesn't exist)
86       */
87      public static String getProperty(Properties props, String propname, String defVal) {
88          String prop;
89          if ((prop = System.getProperty(propname)) != null) {
90              return (prop);
91          }
92          if (props != null && (prop = props.getProperty(propname)) != null) {
93              return (prop);
94          }
95          return defVal;
96      }
97  
98      /**
99       * load a properties file into a Properties object
100      *
101      * @param propertyPaths    the directories to search for the properties file
102      * @param propertyFileName name of the sought properties file
103      * @return Properties object
104      */
105     public static Properties loadProperties(String propertyPaths[], String propertyFileName) {
106         return (loadProperties(propertyPaths, propertyFileName, false, null));
107     }
108 
109     /**
110      * load a properties file into a Properties object
111      *
112      * @param propertyPaths    the directories to search for the properties file
113      * @param propertyFileName name of the sought properties file
114      * @return Properties object
115      */
116     public static Properties loadProperties(String propertyPaths[], String propertyFileName, boolean showName) {
117         return (loadProperties(propertyPaths, propertyFileName, showName, null));
118     }
119 
120     /**
121      * load a properties file into a Properties object
122      *
123      * @param fullFilenameURLStr String representation of url to properties file whether it is in a local file or a resource
124      * @return Properties object
125      */
126     public static Properties loadProperties(String fullFilenameURLStr) {
127         InputStream in = getPropertyFileInputStream(fullFilenameURLStr);
128         String errmsg = "Fatal error: Unable to find specified properties file: " + fullFilenameURLStr;
129 
130         // load the properties
131         Properties props = new Properties();
132         try {
133             if (fullFilenameURLStr.endsWith(".xml") || fullFilenameURLStr.endsWith(".XML")) {
134                 props.loadFromXML(in);
135             } else {
136                 props.load(in);
137             }
138             in.close();
139         } catch (IOException e) {
140             throw new IllegalArgumentException(errmsg);
141         }
142         return props;
143     }
144 
145     /**
146      * load a properties file into a Properties object
147      *
148      * @param propertyPaths    the directories to search for the properties file
149      * @param propertyFileName name of the sought properties file
150      * @param showName         whether the name of the file/resource being read should be shown.
151      * @return Properties object
152      */
153     public static Properties loadProperties(String propertyPaths[], String propertyFileName, boolean showName, String filenameProperty) {
154         String inputStreamSource[] = new String[]{null};
155         InputStream in = getPropertyFileInputStream(propertyPaths, propertyFileName, showName, inputStreamSource);
156         String errmsg = "Fatal error: Unable to find specified properties file: " + propertyFileName;
157 
158         // load the properties
159         Properties props = new Properties();
160         try {
161             if (propertyFileName.endsWith(".xml") || propertyFileName.endsWith(".XML")) {
162                 props.loadFromXML(in);
163             } else {
164                 props.load(in);
165             }
166             in.close();
167             if (filenameProperty != null && inputStreamSource[0] != null) {
168                 File tmpFile = new File(inputStreamSource[0]);
169 
170                 props.setProperty(filenameProperty, tmpFile.getParent());
171             }
172         } catch (IOException e) {
173             throw new IllegalArgumentException(errmsg);
174         }
175         return props;
176     }
177 
178     public static InputStream getPropertyFileInputStream(String[] propertyPaths, String propertyFileName) {
179         return (getPropertyFileInputStream(propertyPaths, propertyFileName, false));
180     }
181 
182     public static InputStream getPropertyFileInputStream(String[] propertyPaths, String propertyFileName, boolean showName) {
183         return (getPropertyFileInputStream(propertyPaths, propertyFileName, false, null));
184     }
185 
186     public static InputStream getPropertyFileInputStream(String propertyFileURLStr) {
187         InputStream in = null;
188         String errmsg = "Fatal error: Unable to open specified properties file: " + propertyFileURLStr;
189         try {
190             URL url = new URL(propertyFileURLStr);
191             in = url.openStream();
192         } catch (IOException e) {
193             throw new IllegalArgumentException(errmsg);
194         }
195 
196         return (in);
197     }
198 
199     public static InputStream getPropertyFileInputStream(String[] propertyPaths, String propertyFileName, boolean showName, String inputSource[]) {
200         InputStream in = null;
201         String fullPropertyFileURLStr = getPropertyFileAbsoluteURL(propertyPaths, propertyFileName, showName, inputSource);
202         return (getPropertyFileInputStream(fullPropertyFileURLStr));
203     }
204 
205 //        String verboseStr = System.getProperty("marc.test.verbose");
206 //        boolean verbose = (verboseStr != null && verboseStr.equalsIgnoreCase("true"));
207 //        String lookedIn = "";
208 //        if (propertyPaths != null)
209 //        {
210 //            File propertyFile = new File(propertyFileName);
211 //            int pathCnt = 0;
212 //            do
213 //            {
214 //                if (propertyFile.exists() && propertyFile.isFile() && propertyFile.canRead())
215 //                {
216 //                    try
217 //                    {
218 //                        in = new FileInputStream(propertyFile);
219 //                        if (inputSource != null && inputSource.length >= 1)
220 //                        {
221 //                            inputSource[0] = propertyFile.getAbsolutePath();
222 //                        }
223 //                        if (showName)
224 //                            logger.info("Opening file: "+ propertyFile.getAbsolutePath());
225 //                        else
226 //                            logger.debug("Opening file: "+ propertyFile.getAbsolutePath());
227 //                    }
228 //                    catch (FileNotFoundException e)
229 //                    {
230 //                        // simply eat this exception since we should only try to open the file if we previously
231 //                        // determined that the file exists and is readable.
232 //                    }
233 //                    break;   // we found it!
234 //                }
235 //                if (verbose)  lookedIn = lookedIn + propertyFile.getAbsolutePath() + "\n";
236 //                if (propertyPaths != null && pathCnt < propertyPaths.length)
237 //                {
238 //                    propertyFile = new File(propertyPaths[pathCnt], propertyFileName);
239 //                }
240 //                pathCnt++;
241 //            } while (propertyPaths != null && pathCnt <= propertyPaths.length);
242 //        }
243 //        // if we didn't find it as a file, look for it as a URL
244 //        String errmsg = "Fatal error: Unable to find specified properties file: " + propertyFileName;
245 //        if (verbose) errmsg = errmsg + "\n Looked in: "+ lookedIn;
246 //        if (in == null)
247 //        {
248 //            Utils utilObj = new Utils();
249 //            URL url = utilObj.getClass().getClassLoader().getResource(propertyFileName);
250 //            if (url == null)
251 //                url = utilObj.getClass().getResource("/" + propertyFileName);
252 //            if (url == null)
253 //            {
254 //                logger.error(errmsg);
255 //                throw new IllegalArgumentException(errmsg);
256 //            }
257 //            if (showName)
258 //                logger.info("Opening resource via URL: "+ url.toString());
259 //            else
260 //                logger.debug("Opening resource via URL: "+ url.toString());
261 //
262 ///*
263 //            if (url == null)
264 //                url = utilObj.getClass().getClassLoader().getResource(propertyPath + "/" + propertyFileName);
265 //            if (url == null)
266 //                url = utilObj.getClass().getResource("/" + propertyPath + "/" + propertyFileName);
267 //*/
268 //            if (url != null)
269 //            {
270 //                try
271 //                {
272 //                    in = url.openStream();
273 //                }
274 //                catch (IOException e)
275 //                {
276 //                    throw new IllegalArgumentException(errmsg);
277 //                }
278 //            }
279 //        }
280 //        return(in);
281 //    }
282 
283     public static String getPropertyFileAbsoluteURL(String[] propertyPaths, String propertyFileName, boolean showName, String inputSource[]) {
284         InputStream in = null;
285         // look for properties file in paths
286         String verboseStr = System.getProperty("marc.test.verbose");
287         boolean verbose = (verboseStr != null && verboseStr.equalsIgnoreCase("true"));
288         String lookedIn = "";
289         String fullPathName = null;
290         if (propertyPaths != null) {
291             File propertyFile = new File(propertyFileName);
292             int pathCnt = 0;
293             do {
294                 if (propertyFile.exists() && propertyFile.isFile() && propertyFile.canRead()) {
295                     try {
296                         fullPathName = propertyFile.toURI().toURL().toExternalForm();
297                         if (inputSource != null && inputSource.length >= 1) {
298                             inputSource[0] = propertyFile.getAbsolutePath();
299                         }
300                     } catch (MalformedURLException e) {
301                         // TODO Auto-generated catch block
302                         e.printStackTrace();
303                     }
304                     if (showName)
305                         logger.info("Opening file: " + propertyFile.getAbsolutePath());
306                     else
307                         logger.debug("Opening file: " + propertyFile.getAbsolutePath());
308                     break;   // we found it!
309                 }
310                 if (verbose) lookedIn = lookedIn + propertyFile.getAbsolutePath() + "\n";
311                 if (propertyPaths != null && pathCnt < propertyPaths.length) {
312                     propertyFile = new File(propertyPaths[pathCnt], propertyFileName);
313                 }
314                 pathCnt++;
315             } while (propertyPaths != null && pathCnt <= propertyPaths.length);
316         }
317         // if we didn't find it as a file, look for it as a URL
318         String errmsg = "Fatal error: Unable to find specified properties file: " + propertyFileName;
319         if (verbose) errmsg = errmsg + "\n Looked in: " + lookedIn;
320         if (fullPathName == null) {
321             Utils utilObj = new Utils();
322             URL url = utilObj.getClass().getClassLoader().getResource(propertyFileName);
323             if (url == null)
324                 url = utilObj.getClass().getResource("/" + propertyFileName);
325             if (url == null) {
326                 logger.error(errmsg);
327                 throw new IllegalArgumentException(errmsg);
328             }
329             if (showName)
330                 logger.info("Opening resource via URL: " + url.toString());
331             else
332                 logger.debug("Opening resource via URL: " + url.toString());
333 
334 /*
335             if (url == null)
336                 url = utilObj.getClass().getClassLoader().getResource(propertyPath + "/" + propertyFileName);
337             if (url == null)
338                 url = utilObj.getClass().getResource("/" + propertyPath + "/" + propertyFileName);
339 */
340             fullPathName = url.toExternalForm();
341         }
342         return (fullPathName);
343     }
344 
345 
346     /**
347      * Takes an InputStream, reads the entire contents into a String
348      *
349      * @param stream - the stream to read in.
350      * @return String containing entire contents of stream.
351      */
352     public static String readStreamIntoString(InputStream stream) throws IOException {
353         Reader in = new BufferedReader(new InputStreamReader(stream));
354 
355         StringBuilder sb = new StringBuilder();
356         char[] chars = new char[4096];
357         int length;
358 
359         while ((length = in.read(chars)) > 0) {
360             sb.append(chars, 0, length);
361         }
362 
363         return sb.toString();
364     }
365 
366 
367     /**
368      * Cleans non-digits from a String
369      *
370      * @param date String to parse
371      * @return Numeric part of date String (or null)
372      */
373     public static String cleanDate(final String date) {
374         matcher_braces = FOUR_DIGIT_PATTERN_BRACES.matcher(date);
375         matcher_one_brace = FOUR_DIGIT_PATTERN_ONE_BRACE.matcher(date);
376         matcher_start_with_1_2 = FOUR_DIGIT_PATTERN_STARTING_WITH_1_2.matcher(date);
377         matcher_l_plus_three_digits = FOUR_DIGIT_PATTERN_OTHER_1.matcher(date);
378         matcher_bracket_19_plus_two_digits = FOUR_DIGIT_PATTERN_OTHER_2.matcher(date);
379         matcher_three_digits_plus_unk = FOUR_DIGIT_PATTERN_OTHER_3.matcher(date);
380         matcher_ie_date = FOUR_DIGIT_PATTERN_OTHER_4.matcher(date);
381         matcher = FOUR_DIGIT_PATTERN.matcher(date);
382         matcher_bc_date = BC_DATE_PATTERN.matcher(date);
383 
384         String cleanDate = null; // raises DD-anomaly
385 
386         if (matcher_braces.find()) {
387             cleanDate = matcher_braces.group();
388             cleanDate = Utils.removeOuterBrackets(cleanDate);
389             if (matcher.find()) {
390                 String tmp = matcher.group();
391                 if (!tmp.equals(cleanDate)) {
392                     tmp = "" + tmp;
393                 }
394             }
395         } else if (matcher_ie_date.find()) {
396             cleanDate = matcher_ie_date.group().replaceAll("i.e. ", "");
397         } else if (matcher_one_brace.find()) {
398             cleanDate = matcher_one_brace.group();
399             cleanDate = Utils.removeOuterBrackets(cleanDate);
400             if (matcher.find()) {
401                 String tmp = matcher.group();
402                 if (!tmp.equals(cleanDate)) {
403                     tmp = "" + tmp;
404                 }
405             }
406         } else if (matcher_bc_date.find()) {
407             cleanDate = null;
408         } else if (matcher_start_with_1_2.find()) {
409             cleanDate = matcher_start_with_1_2.group();
410         } else if (matcher_l_plus_three_digits.find()) {
411             cleanDate = matcher_l_plus_three_digits.group().replaceAll("l", "1");
412         } else if (matcher_bracket_19_plus_two_digits.find()) {
413             cleanDate = matcher_bracket_19_plus_two_digits.group().replaceAll("\\[", "").replaceAll("\\]", "");
414         } else if (matcher_three_digits_plus_unk.find()) {
415             cleanDate = matcher_three_digits_plus_unk.group().replaceAll("[-?]", "0");
416         }
417         if (cleanDate != null) {
418             Calendar calendar = Calendar.getInstance();
419             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy");
420             String thisYear = dateFormat.format(calendar.getTime());
421             try {
422                 if (Integer.parseInt(cleanDate) > Integer.parseInt(thisYear) + 1)
423                     cleanDate = null;
424             } catch (NumberFormatException nfe) {
425                 cleanDate = null;
426             }
427         }
428         if (cleanDate != null) {
429             logger.debug("Date : " + date + " mapped to : " + cleanDate);
430         } else {
431             logger.debug("No Date match: " + date);
432         }
433         return cleanDate;
434     }
435 
436     /**
437      * Removes trailing characters (space, comma, slash, semicolon, colon),
438      * trailing period if it is preceded by at least three letters,
439      * and single square bracket characters if they are the start and/or end
440      * chars of the cleaned string
441      *
442      * @param origStr String to clean
443      * @return cleaned string
444      */
445     public static String cleanData(String origStr) {
446         String currResult = origStr;
447         String prevResult;
448         do {
449             prevResult = currResult;
450             currResult = currResult.trim();
451 
452             currResult = currResult.replaceAll(" *([,/;:])$", "");
453 
454             // trailing period removed in certain circumstances
455             if (currResult.endsWith(".")) {
456                 if (currResult.matches(".*[JS]r\\.$")) {
457                     // dont strip period off of Jr. or Sr.
458                 } else if (currResult.matches(".*\\w\\w\\.$")) {
459                     currResult = currResult.substring(0, currResult.length() - 1);
460                 } else if (currResult.matches(".*\\p{L}\\p{L}\\.$")) {
461                     currResult = currResult.substring(0, currResult.length() - 1);
462                 } else if (currResult.matches(".*\\w\\p{InCombiningDiacriticalMarks}?\\w\\p{InCombiningDiacriticalMarks}?\\.$")) {
463                     currResult = currResult.substring(0, currResult.length() - 1);
464                 } else if (currResult.matches(".*\\p{Punct}\\.$")) {
465                     currResult = currResult.substring(0, currResult.length() - 1);
466                 }
467             }
468 
469             currResult = removeOuterBrackets(currResult);
470 
471             if (currResult.length() == 0)
472                 return currResult;
473 
474         } while (!currResult.equals(prevResult));
475 
476 //        if (!currResult.equals(origStr))
477 //            System.out.println(origStr + " -> "+ currResult);
478 
479         return currResult;
480     }
481 
482     /**
483      * Call cleanData on an entire set of Strings has a side effect
484      * of deleting entries that are identical when they are cleaned.
485      *
486      * @param values - the set to clean
487      * @return Set<String> - the "same" set with all of its entries cleaned.
488      */
489     private static Set<String> cleanData(Set<String> values) {
490         Set<String> result = new LinkedHashSet<String>();
491         for (String entry : values) {
492             String cleaned = cleanData(entry);
493             result.add(cleaned);
494         }
495         return (result);
496     }
497 
498 
499     /**
500      * Repeatedly removes trailing characters indicated in regular expression,
501      * PLUS trailing period if it is preceded by its regular expression
502      *
503      * @param origStr            String to clean
504      * @param trailingCharsRegEx a regular expression of trailing chars to be
505      *                           removed (see java Pattern class).  Note that the regular expression
506      *                           should NOT have '$' at the end.
507      *                           (e.g. " *[,/;:]" replaces any commas, slashes, semicolons or colons
508      *                           at the end of the string, and these chars may optionally be preceded
509      *                           by a space)
510      * @param charsB4periodRegEx a regular expression that must immediately
511      *                           precede a trailing period IN ORDER FOR THE PERIOD TO BE REMOVED.
512      *                           Note that the regular expression will NOT have the period or '$' at
513      *                           the end.
514      *                           (e.g. "[a-zA-Z]{3,}" means at least three letters must immediately
515      *                           precede the period for it to be removed.)
516      * @return cleaned string
517      */
518     public static String removeAllTrailingCharAndPeriod(String origStr, String trailingCharsRegEx, String charsB4periodRegEx) {
519         if (origStr == null)
520             return null;
521 
522         String currResult = origStr;
523         String prevResult;
524         do {
525             prevResult = currResult;
526             currResult = removeTrailingCharAndPeriod(currResult.trim(), trailingCharsRegEx, charsB4periodRegEx);
527 
528             if (currResult.length() == 0)
529                 return currResult;
530 
531         } while (!currResult.equals(prevResult));
532 
533         return currResult;
534     }
535 
536     /**
537      * Removes trailing characters indicated in regular expression, PLUS
538      * trailing period if it is preceded by its regular expression.
539      *
540      * @param origStr            String to clean
541      * @param trailingCharsRegEx a regular expression of trailing chars to be
542      *                           removed (see java Pattern class).  Note that the regular expression
543      *                           should NOT have '$' at the end.
544      *                           (e.g. " *[,/;:]" replaces any commas, slashes, semicolons or colons
545      *                           at the end of the string, and these chars may optionally be preceded
546      *                           by a space)
547      * @param charsB4periodRegEx a regular expression that must immediately
548      *                           precede a trailing period IN ORDER FOR THE PERIOD TO BE REMOVED.
549      *                           Note that the regular expression will NOT have the period or '$' at
550      *                           the end.
551      *                           (e.g. "[a-zA-Z]{3,}" means at least three letters must immediately
552      *                           precede the period for it to be removed.)
553      * @return cleaned string
554      */
555     public static String removeTrailingCharAndPeriod(String origStr, String trailingCharsRegEx, String charsB4periodRegEx) {
556         if (origStr == null)
557             return null;
558 
559         String result = removeTrailingChar(origStr, trailingCharsRegEx);
560 
561         result = removeTrailingPeriod(result, charsB4periodRegEx);
562 
563         return result;
564     }
565 
566     /**
567      * Remove the characters per the regular expression if they are at the end
568      * of the string.
569      *
570      * @param origStr             string to be cleaned
571      * @param charsToReplaceRegEx - a regular expression of the trailing string/chars
572      *                            to be removed e.g. " *([,/;:])" meaning last character is a comma,
573      *                            slash, semicolon, colon, possibly preceded by one or more spaces.
574      * @return the string with the specified trailing characters removed
575      * @see java.util.regex.Pattern class in java api
576      */
577     public static String removeTrailingChar(String origStr, String charsToReplaceRegEx) {
578         if (origStr == null)
579             return origStr;
580         // get rid of reg ex specified chars at the end of the string
581         return origStr.trim().replaceAll(charsToReplaceRegEx + "$", "");
582     }
583 
584     /**
585      * If there is a period at the end of the string, remove the period if it is
586      * immediately preceded by the regular expression
587      *
588      * @param origStr            the string to be cleaned
589      * @param charsB4periodRegEx a regular expression that must immediately
590      *                           precede a trailing period IN ORDER FOR THE PERIOD TO BE REMOVED.
591      *                           Note that the regular expression will NOT have the period or '$' at
592      *                           the end.
593      *                           (e.g. "[a-zA-Z]{3,}" means at least three letters must immediately
594      *                           precede the period for it to be removed.)
595      * @return the string without a trailing period iff the regular expression
596      *         param was found immediately before the trailing period
597      */
598     public static String removeTrailingPeriod(String origStr, String precedingCharsRegEx) {
599         if (origStr == null)
600             return origStr;
601         String result = origStr.trim();
602         if (result.endsWith(".") && result.matches(".*" + precedingCharsRegEx + "\\.$"))
603             result = result.substring(0, result.length() - 1).trim();
604 
605         return result;
606     }
607 
608 
609     /**
610      * Remove single square bracket characters if they are the start and/or end
611      * chars (matched or unmatched) and are the only square bracket chars in
612      * the string.
613      */
614     public static String removeOuterBrackets(String origStr) {
615         if (origStr == null || origStr.length() == 0)
616             return origStr;
617 
618         String result = origStr.trim();
619 
620         if (result.length() > 0) {
621             boolean openBracketFirst = result.charAt(0) == '[';
622             boolean closeBracketLast = result.endsWith("]");
623             if (openBracketFirst && closeBracketLast &&
624                     result.indexOf('[', 1) == -1 &&
625                     result.lastIndexOf(']', result.length() - 2) == -1)
626                 // only square brackets are at beginning and end
627                 result = result.substring(1, result.length() - 1);
628             else if (openBracketFirst && result.indexOf(']') == -1)
629                 // starts with '[' but no ']'; remove open bracket
630                 result = result.substring(1);
631             else if (closeBracketLast && result.indexOf('[') == -1)
632                 // ends with ']' but no '['; remove close bracket
633                 result = result.substring(0, result.length() - 1);
634         }
635 
636         return result.trim();
637     }
638 
639 
640     /**
641      * Calculate time from milliseconds
642      *
643      * @param totalTime Time in milliseconds
644      * @return Time in the format mm:ss.ss
645      */
646     public static String calcTime(final long totalTime) {
647         return totalTime / 60000 + ":" + timeFormat.format((totalTime % 60000) / 1000);
648     }
649 
650     /**
651      * Test if a String has a numeric equivalent
652      *
653      * @param number String representation of a number
654      * @return True if String is a number; False if it is not
655      */
656     public static boolean isNumber(final String number) {
657         boolean isNumber; // fix for dd-anomaly
658 
659         try {
660             Integer.parseInt(number);
661             isNumber = true;
662         } catch (NumberFormatException nfe) {
663             // eat the exception
664             isNumber = false;
665         }
666 
667         return isNumber;
668     }
669 
670     /**
671      * Remap a field value.  If the field value is not present in the map, then:
672      * if "displayRawIfMissing" is a key in the map, then the raw field value
673      * is used.
674      * if "displayRawIfMissing" is not a key in the map, and the allowDefault
675      * param is set to true, then if the map contains "__DEFAULT" as a key,
676      * the value of "__DEFAULT" in the map is used;  if allowDefault is true
677      * and there is neither "displayRawIfMissing" nor "__DEFAULT", as a key
678      * in the map, then if the map contains an empty key, the map value of the
679      * empty key is used.
680      * NOTE:  If the spec for a field is supposed to contain all matching
681      * values, then the default lookup needs to be done here.  If the spec
682      * for a field is only supposed to return the first matching mappable
683      * value, then the default mapping should be done in the calling method
684      *
685      * @param fieldVal     - the raw value to be mapped
686      * @param map          - the map to be used
687      * @param allowDefault - if "displayRawIfMissing" is not a key in the map,
688      *                     and this is to true, then if the map contains "__DEFAULT" as a key,
689      *                     the value of "__DEFAULT" in the map is used.
690      * @return the new value, as determined by the mapping.
691      */
692     public static String remap(String fieldVal, Map<String, String> map, boolean allowDefault) {
693         String result = null;
694 
695         if (map.keySet().contains("pattern_0")) {
696             for (int i = 0; i < map.keySet().size(); i++) {
697                 String patternStr = map.get("pattern_" + i);
698                 String parts[] = patternStr.split("=>");
699                 if (containsMatch(fieldVal, parts[0])) {
700                     String newVal = parts[1];
701                     if (parts[1].contains("$")) {
702                         newVal = fieldVal.replaceAll(parts[0], parts[1]);
703                         fieldVal = newVal;
704                     }
705                     result = newVal;
706                 }
707             }
708         }
709         if (map.containsKey(fieldVal)) {
710             result = map.get(fieldVal);
711         } else if (map.containsKey("displayRawIfMissing")) {
712             result = fieldVal;
713         } else if (allowDefault && map.containsKey("__DEFAULT")) {
714             result = map.get("__DEFAULT");
715         } else if (allowDefault && map.containsKey("")) {
716             result = map.get("");
717         }
718         if (result == null || result.length() == 0) return null;
719         return result;
720     }
721 
722     /**
723      * Remap a set of field values.  If a field value is not present in the map,
724      * then:
725      * if "displayRawIfMissing" is a key in the map, then the raw field value
726      * is used.
727      * if "displayRawIfMissing" is not a key in the map, and the allowDefault
728      * param is set to true, then if the map contains "__DEFAULT" as a key,
729      * the value of "__DEFAULT" in the map is used;  if allowDefault is true
730      * and there is neither "displayRawIfMissing" nor "__DEFAULT", as a key
731      * in the map, then if the map contains an empty key, the map value of the
732      * empty key is used.
733      * NOTE:  If the spec for a field is supposed to contain all matching
734      * values, then the default lookup needs to be done here.  If the spec
735      * for a field is only supposed to return the first matching mappable
736      * value, then the default mapping should be done in the calling method
737      *
738      * @param fieldVal     - the raw value to be mapped
739      * @param map          - the map to be used
740      * @param allowDefault - if "displayRawIfMissing" is not a key in the map,
741      *                     and this is to true, then if the map contains "__DEFAULT" as a key,
742      *                     the value of "__DEFAULT" in the map is used.
743      * @return the new value, as determined by the mapping.
744      */
745     public static Set<String> remap(Set<String> set, Map<String, String> map, boolean allowDefault) {
746         if (map == null) return (set);
747         Iterator<String> iter = set.iterator();
748         Set<String> result = new LinkedHashSet<String>();
749 
750         while (iter.hasNext()) {
751             String val = iter.next();
752             if (map.keySet().contains("pattern_0")) {
753                 String tmpResult = null;
754                 for (int i = 0; i < map.keySet().size(); i++) {
755                     String patternStr = map.get("pattern_" + i);
756                     String parts[] = patternStr.split("=>");
757                     if (containsMatch(val, parts[0])) {
758                         String newVal = parts[1];
759                         if (parts[1].contains("$")) {
760                             newVal = val.replaceAll(parts[0], parts[1]);
761                             val = newVal;
762                         } else {
763                             result.add(newVal);
764                         }
765                         tmpResult = newVal;
766                     }
767                 }
768                 if (tmpResult != null) result.add(tmpResult);
769             } else {
770                 String mappedVal = remap(val, map, allowDefault);
771                 if (mappedVal != null) {
772                     if (mappedVal.contains("|")) {
773                         String vals[] = mappedVal.split("[|]");
774                         for (String oneVal : vals) {
775                             result.add(oneVal);
776                         }
777                     } else
778                         result.add(mappedVal);
779                 }
780             }
781         }
782         return result;
783     }
784 
785     private static boolean containsMatch(String val, String pattern) {
786         String rep = val.replaceFirst(pattern, "###match###");
787 
788         if (!rep.equals(val)) {
789             return true;
790         }
791 
792         return false;
793     }
794 
795     /**
796      * Test if a set contains a specified pattern
797      *
798      * @param set     Set of marc fields to test
799      * @param pattern Regex String pattern to match
800      * @return If the set contains the pattern, return true, else false
801      */
802     public static boolean setItemContains(Set<String> set, String pattern) {
803         if (set.isEmpty()) {
804             return (false);
805         }
806 
807         Iterator<String> iter = set.iterator();
808 
809         while (iter.hasNext()) {
810             String value = (String) iter.next();
811 
812             if (containsMatch(value, pattern)) {
813                 return true;
814             }
815 
816         }
817         return false;
818     }
819 
820     /**
821      * Join two fields together with seperator
822      *
823      * @param set       Set of marc fields to join
824      * @param separator Separation character to put between
825      * @return Joined fields
826      */
827     public static String join(Set<String> set, String separator) {
828         Iterator<String> iter = set.iterator();
829         //String result = "";
830         StringBuffer result = new StringBuffer("");
831 
832         while (iter.hasNext()) {
833             //result += iter.next();
834             result.append(iter.next());
835             if (iter.hasNext()) {
836                 //result += separator;
837                 result.append(separator);
838             }
839         }
840 
841         return result.toString();
842     }
843 
844     public static Set<String> trimNearDuplicates(Set<String> locations) {
845         locations = cleanData(locations);
846         if (locations.size() <= 1) return (locations);
847         Object locArr[] = locations.toArray();
848         int size = locArr.length;
849         for (int i = 0; i < size; i++) {
850             boolean copyStrI = true;
851             for (int j = 0; j < size; j++) {
852                 if (i == j) continue;
853                 if (locArr[j].toString().contains(locArr[i].toString())) {
854                     copyStrI = false;
855                     break;
856                 }
857             }
858             if (copyStrI == false) locations.remove(locArr[i]);
859         }
860         return locations;
861     }
862 
863 
864     /**
865      * returns true if the 3 letter language code is for a right to left
866      * language (one written in arabic or hebrew characters)
867      *
868      * @param langcode
869      * @return
870      */
871     public final static boolean isRightToLeftLanguage(String langcode) {
872         if (
873             // arabic characters
874                 langcode.equals("ara") || langcode.equals("per") || langcode.equals("urd")
875                         ||
876                         // hebrew characters
877                         langcode.equals("heb") || langcode.equals("yid") || langcode.equals("lad")
878                         || langcode.equals("jpr") || langcode.equals("jrb")
879                 )
880             return true;
881         else
882             return false;
883     }
884 
885 
886     /**
887      * return the index within this string of the first occurrence of an open
888      * parenthesis that isn't escaped with a backslash.
889      *
890      * @param str
891      * @return if an unescaped open parenthesis occurs within this object,
892      *         return the index of the first open paren; -1 if no unescaped open paren.
893      */
894     public final static int getIxUnescapedOpenParen(String str) {
895         if (str.startsWith("("))
896             return 0;
897         Pattern p = Pattern.compile(".*[^\\\\](\\().*");
898         Matcher m = p.matcher(str);
899         if (m.matches())
900             return m.start(1);
901         else
902             return -1;
903     }
904 
905 
906     /**
907      * return the index within this string of the first occurrence of a comma
908      * that isn't escaped with a backslash.
909      *
910      * @param str
911      * @return if an unescaped comma occurs within this object, the index of the
912      *         first comma; -1 if no unescaped comma.
913      */
914     public final static int getIxUnescapedComma(String str) {
915         if (str.startsWith(","))
916             return 0;
917         Pattern p = Pattern.compile(".*[^\\\\](,).*");
918         Matcher m = p.matcher(str);
919         if (m.matches())
920             return m.start(1);
921         else
922             return -1;
923     }
924 
925     /**
926      * Look for Strings in the set, that start with the given prefix.  If found,
927      * remove the prefix, trim the result and add it to the returned set of
928      * Strings to be returned.
929      *
930      * @param valueSet
931      * @param prefix
932      * @return set members that had the prefix, but now prefix is removed and
933      *         remaining value is trimmed.
934      */
935     public final static Set<String> getPrefixedVals(Set<String> valueSet, String prefix) {
936         Set<String> resultSet = new LinkedHashSet<String>();
937         if (!valueSet.isEmpty()) {
938             Iterator<String> iter = valueSet.iterator();
939             while (iter.hasNext()) {
940                 String s = removePrefix((String) iter.next(), prefix);
941                 if (s != null) {
942                     String value = s.trim();
943                     if (value != null && value.length() != 0)
944                         resultSet.add(value);
945                 }
946             }
947         }
948         return resultSet;
949     }
950 
951     /**
952      * remove prefix from the beginning of the value string.
953      */
954     public final static String removePrefix(String value, String prefix) {
955         if (value.startsWith(prefix)) {
956             value = value.substring(prefix.length());
957             if (value != null && value.length() != 0)
958                 return value;
959         }
960         return null;
961     }
962 
963     /**
964      * returns the valid ISBN(s) from the set of candidate Strings
965      *
966      * @return Set of strings containing valid ISBN numbers
967      */
968     public static Set<String> returnValidISBNs(Set<String> candidates) {
969         // NOTE 1: last digit of ISBN is a check digit and may be "X" (0,1,2,3,4,5,6.7.8.9.X)
970         // NOTE 2: ISBN can be 10 or 13 digits (and may end with X).
971         // NOTE 3: 13 digit ISBN must start with 978 or 979.
972         // NOTE 4: there may be text after the ISBN, which should be removed
973 
974         Set<String> isbnSet = new LinkedHashSet<String>();
975 
976         Pattern p10 = Pattern.compile("^\\d{9}[\\dX].*");
977         Pattern p13 = Pattern.compile("^(978|979)\\d{9}[X\\d].*");
978         // p13any matches a 13 digit isbn pattern without the correct prefix
979         Pattern p13any = Pattern.compile("^\\d{12}[X\\d].*");
980 
981         Iterator<String> iter = candidates.iterator();
982         while (iter.hasNext()) {
983             String value = (String) iter.next().trim();
984             // check we have the right pattern, and remove trailing text
985             if (p13.matcher(value).matches())
986                 isbnSet.add(value.substring(0, 13));
987             else if (p10.matcher(value).matches() && !p13any.matcher(value).matches())
988                 isbnSet.add(value.substring(0, 10));
989         }
990         return isbnSet;
991     }
992 
993 
994     /**
995      * For each occurrence of a marc field in the tags list, extract all
996      * subfield data from the field, place it in a single string (individual
997      * subfield data separated by spaces) and add the string to the result set.
998      */
999     @SuppressWarnings("unchecked")
1000     public static final Set<String> getAllSubfields(final Record record, String[] tags) {
1001         Set<String> result = new LinkedHashSet<String>();
1002 
1003         List<VariableField> varFlds = record.getVariableFields(tags);
1004         for (VariableField vf : varFlds) {
1005 
1006             StringBuffer buffer = new StringBuffer(500);
1007 
1008             DataField df = (DataField) vf;
1009             if (df != null) {
1010                 List<Subfield> subfields = df.getSubfields();
1011                 for (Subfield sf : subfields) {
1012                     if (buffer.length() > 0) {
1013                         buffer.append(" " + sf.getData());
1014                     } else {
1015                         buffer.append(sf.getData());
1016                     }
1017                 }
1018             }
1019             if (buffer.length() > 0)
1020                 result.add(buffer.toString());
1021         }
1022 
1023         return result;
1024     }
1025 
1026     /**
1027      * get the contents of a subfield, rigorously ensuring no NPE
1028      *
1029      * @param df   - DataField of interest
1030      * @param code - code of subfield of interest
1031      * @return the contents of the subfield, if it exists; null otherwise
1032      */
1033     public static final String getSubfieldData(DataField df, char code) {
1034         String result = null;
1035         if (df != null) {
1036             Subfield sf = df.getSubfield(code);
1037             if (sf != null && sf.getData() != null) {
1038                 result = sf.getData();
1039             }
1040         }
1041         return result;
1042     }
1043 
1044     /**
1045      * returns all values of subfield strings of a particular code
1046      * contained in the data field
1047      */
1048     @SuppressWarnings("unchecked")
1049     public static final List<String> getSubfieldStrings(DataField df, char code) {
1050         List<Subfield> listSubcode = df.getSubfields(code);
1051         List<String> vals = new ArrayList(listSubcode.size());
1052         for (Subfield s : listSubcode) {
1053             vals.add(s.getData());
1054         }
1055         return vals;
1056     }
1057 
1058 
1059     /**
1060      * given a latin letter with a diacritic, return the latin letter without
1061      * the diacritic.
1062      * Shamelessly stolen from UnicodeCharUtil class of UnicodeNormalizeFilter
1063      * by Bob Haschart
1064      */
1065     public static char foldDiacriticLatinChar(char c) {
1066         switch (c) {
1067             case 0x0181:
1068                 return (0x0042);    //  LATIN CAPITAL LETTER B WITH HOOK -> LATIN CAPITAL LETTER B
1069             case 0x0182:
1070                 return (0x0042);    //  LATIN CAPITAL LETTER B WITH TOPBAR -> LATIN CAPITAL LETTER B
1071             case 0x0187:
1072                 return (0x0043);    //  LATIN CAPITAL LETTER C WITH HOOK -> LATIN CAPITAL LETTER C
1073             case 0x0110:
1074                 return (0x0044);    //  LATIN CAPITAL LETTER D WITH STROKE -> LATIN CAPITAL LETTER D
1075             case 0x018A:
1076                 return (0x0044);    //  LATIN CAPITAL LETTER D WITH HOOK -> LATIN CAPITAL LETTER D
1077             case 0x018B:
1078                 return (0x0044);    //  LATIN CAPITAL LETTER D WITH TOPBAR -> LATIN CAPITAL LETTER D
1079             case 0x0191:
1080                 return (0x0046);    //  LATIN CAPITAL LETTER F WITH HOOK -> LATIN CAPITAL LETTER F
1081             case 0x0193:
1082                 return (0x0047);    //  LATIN CAPITAL LETTER G WITH HOOK -> LATIN CAPITAL LETTER G
1083             case 0x01E4:
1084                 return (0x0047);    //  LATIN CAPITAL LETTER G WITH STROKE -> LATIN CAPITAL LETTER G
1085             case 0x0126:
1086                 return (0x0048);    //  LATIN CAPITAL LETTER H WITH STROKE -> LATIN CAPITAL LETTER H
1087             case 0x0197:
1088                 return (0x0049);    //  LATIN CAPITAL LETTER I WITH STROKE -> LATIN CAPITAL LETTER I
1089             case 0x0198:
1090                 return (0x004B);    //  LATIN CAPITAL LETTER K WITH HOOK -> LATIN CAPITAL LETTER K
1091             case 0x0141:
1092                 return (0x004C);    //  LATIN CAPITAL LETTER L WITH STROKE -> LATIN CAPITAL LETTER L
1093             case 0x019D:
1094                 return (0x004E);    //  LATIN CAPITAL LETTER N WITH LEFT HOOK -> LATIN CAPITAL LETTER N
1095             case 0x0220:
1096                 return (0x004E);    //  LATIN CAPITAL LETTER N WITH LONG RIGHT LEG -> LATIN CAPITAL LETTER N
1097             case 0x00D8:
1098                 return (0x004F);    //  LATIN CAPITAL LETTER O WITH STROKE -> LATIN CAPITAL LETTER O
1099             case 0x019F:
1100                 return (0x004F);    //  LATIN CAPITAL LETTER O WITH MIDDLE TILDE -> LATIN CAPITAL LETTER O
1101             case 0x01FE:
1102                 return (0x004F);    //  LATIN CAPITAL LETTER O WITH STROKE AND ACUTE -> LATIN CAPITAL LETTER O
1103             case 0x01A4:
1104                 return (0x0050);    //  LATIN CAPITAL LETTER P WITH HOOK -> LATIN CAPITAL LETTER P
1105             case 0x0166:
1106                 return (0x0054);    //  LATIN CAPITAL LETTER T WITH STROKE -> LATIN CAPITAL LETTER T
1107             case 0x01AC:
1108                 return (0x0054);    //  LATIN CAPITAL LETTER T WITH HOOK -> LATIN CAPITAL LETTER T
1109             case 0x01AE:
1110                 return (0x0054);    //  LATIN CAPITAL LETTER T WITH RETROFLEX HOOK -> LATIN CAPITAL LETTER T
1111             case 0x01B2:
1112                 return (0x0056);    //  LATIN CAPITAL LETTER V WITH HOOK -> LATIN CAPITAL LETTER V
1113             case 0x01B3:
1114                 return (0x0059);    //  LATIN CAPITAL LETTER Y WITH HOOK -> LATIN CAPITAL LETTER Y
1115             case 0x01B5:
1116                 return (0x005A);    //  LATIN CAPITAL LETTER Z WITH STROKE -> LATIN CAPITAL LETTER Z
1117             case 0x0224:
1118                 return (0x005A);    //  LATIN CAPITAL LETTER Z WITH HOOK -> LATIN CAPITAL LETTER Z
1119             case 0x0180:
1120                 return (0x0062);    //  LATIN SMALL LETTER B WITH STROKE -> LATIN SMALL LETTER B
1121             case 0x0183:
1122                 return (0x0062);    //  LATIN SMALL LETTER B WITH TOPBAR -> LATIN SMALL LETTER B
1123             case 0x0253:
1124                 return (0x0062);    //  LATIN SMALL LETTER B WITH HOOK -> LATIN SMALL LETTER B
1125             case 0x0188:
1126                 return (0x0063);    //  LATIN SMALL LETTER C WITH HOOK -> LATIN SMALL LETTER C
1127             case 0x0255:
1128                 return (0x0063);    //  LATIN SMALL LETTER C WITH CURL -> LATIN SMALL LETTER C
1129             case 0x0111:
1130                 return (0x0064);    //  LATIN SMALL LETTER D WITH STROKE -> LATIN SMALL LETTER D
1131             case 0x018C:
1132                 return (0x0064);    //  LATIN SMALL LETTER D WITH TOPBAR -> LATIN SMALL LETTER D
1133             case 0x0221:
1134                 return (0x0064);    //  LATIN SMALL LETTER D WITH CURL -> LATIN SMALL LETTER D
1135             case 0x0256:
1136                 return (0x0064);    //  LATIN SMALL LETTER D WITH TAIL -> LATIN SMALL LETTER D
1137             case 0x0257:
1138                 return (0x0064);    //  LATIN SMALL LETTER D WITH HOOK -> LATIN SMALL LETTER D
1139             case 0x0192:
1140                 return (0x0066);    //  LATIN SMALL LETTER F WITH HOOK -> LATIN SMALL LETTER F
1141             case 0x01E5:
1142                 return (0x0067);    //  LATIN SMALL LETTER G WITH STROKE -> LATIN SMALL LETTER G
1143             case 0x0260:
1144                 return (0x0067);    //  LATIN SMALL LETTER G WITH HOOK -> LATIN SMALL LETTER G
1145             case 0x0127:
1146                 return (0x0068);    //  LATIN SMALL LETTER H WITH STROKE -> LATIN SMALL LETTER H
1147             case 0x0266:
1148                 return (0x0068);    //  LATIN SMALL LETTER H WITH HOOK -> LATIN SMALL LETTER H
1149             case 0x0268:
1150                 return (0x0069);    //  LATIN SMALL LETTER I WITH STROKE -> LATIN SMALL LETTER I
1151             case 0x029D:
1152                 return (0x006A);    //  LATIN SMALL LETTER J WITH CROSSED-TAIL -> LATIN SMALL LETTER J
1153             case 0x0199:
1154                 return (0x006B);    //  LATIN SMALL LETTER K WITH HOOK -> LATIN SMALL LETTER K
1155             case 0x0142:
1156                 return (0x006C);    //  LATIN SMALL LETTER L WITH STROKE -> LATIN SMALL LETTER L
1157             case 0x019A:
1158                 return (0x006C);    //  LATIN SMALL LETTER L WITH BAR -> LATIN SMALL LETTER L
1159             case 0x0234:
1160                 return (0x006C);    //  LATIN SMALL LETTER L WITH CURL -> LATIN SMALL LETTER L
1161             case 0x026B:
1162                 return (0x006C);    //  LATIN SMALL LETTER L WITH MIDDLE TILDE -> LATIN SMALL LETTER L
1163             case 0x026C:
1164                 return (0x006C);    //  LATIN SMALL LETTER L WITH BELT -> LATIN SMALL LETTER L
1165             case 0x026D:
1166                 return (0x006C);    //  LATIN SMALL LETTER L WITH RETROFLEX HOOK -> LATIN SMALL LETTER L
1167             case 0x0271:
1168                 return (0x006D);    //  LATIN SMALL LETTER M WITH HOOK -> LATIN SMALL LETTER M
1169             case 0x019E:
1170                 return (0x006E);    //  LATIN SMALL LETTER N WITH LONG RIGHT LEG -> LATIN SMALL LETTER N
1171             case 0x0235:
1172                 return (0x006E);    //  LATIN SMALL LETTER N WITH CURL -> LATIN SMALL LETTER N
1173             case 0x0272:
1174                 return (0x006E);    //  LATIN SMALL LETTER N WITH LEFT HOOK -> LATIN SMALL LETTER N
1175             case 0x0273:
1176                 return (0x006E);    //  LATIN SMALL LETTER N WITH RETROFLEX HOOK -> LATIN SMALL LETTER N
1177             case 0x00F8:
1178                 return (0x006F);    //  LATIN SMALL LETTER O WITH STROKE -> LATIN SMALL LETTER O
1179             case 0x01FF:
1180                 return (0x006F);    //  LATIN SMALL LETTER O WITH STROKE AND ACUTE -> LATIN SMALL LETTER O
1181             case 0x01A5:
1182                 return (0x0070);    //  LATIN SMALL LETTER P WITH HOOK -> LATIN SMALL LETTER P
1183             case 0x02A0:
1184                 return (0x0071);    //  LATIN SMALL LETTER Q WITH HOOK -> LATIN SMALL LETTER Q
1185             case 0x027C:
1186                 return (0x0072);    //  LATIN SMALL LETTER R WITH LONG LEG -> LATIN SMALL LETTER R
1187             case 0x027D:
1188                 return (0x0072);    //  LATIN SMALL LETTER R WITH TAIL -> LATIN SMALL LETTER R
1189             case 0x0282:
1190                 return (0x0073);    //  LATIN SMALL LETTER S WITH HOOK -> LATIN SMALL LETTER S
1191             case 0x0167:
1192                 return (0x0074);    //  LATIN SMALL LETTER T WITH STROKE -> LATIN SMALL LETTER T
1193             case 0x01AB:
1194                 return (0x0074);    //  LATIN SMALL LETTER T WITH PALATAL HOOK -> LATIN SMALL LETTER T
1195             case 0x01AD:
1196                 return (0x0074);    //  LATIN SMALL LETTER T WITH HOOK -> LATIN SMALL LETTER T
1197             case 0x0236:
1198                 return (0x0074);    //  LATIN SMALL LETTER T WITH CURL -> LATIN SMALL LETTER T
1199             case 0x0288:
1200                 return (0x0074);    //  LATIN SMALL LETTER T WITH RETROFLEX HOOK -> LATIN SMALL LETTER T
1201             case 0x028B:
1202                 return (0x0076);    //  LATIN SMALL LETTER V WITH HOOK -> LATIN SMALL LETTER V
1203             case 0x01B4:
1204                 return (0x0079);    //  LATIN SMALL LETTER Y WITH HOOK -> LATIN SMALL LETTER Y
1205             case 0x01B6:
1206                 return (0x007A);    //  LATIN SMALL LETTER Z WITH STROKE -> LATIN SMALL LETTER Z
1207             case 0x0225:
1208                 return (0x007A);    //  LATIN SMALL LETTER Z WITH HOOK -> LATIN SMALL LETTER Z
1209             case 0x0290:
1210                 return (0x007A);    //  LATIN SMALL LETTER Z WITH RETROFLEX HOOK -> LATIN SMALL LETTER Z
1211             case 0x0291:
1212                 return (0x007A);    //  LATIN SMALL LETTER Z WITH CURL -> LATIN SMALL LETTER Z
1213             default:
1214                 return (0x00);
1215         }
1216     }
1217 
1218 //    @SuppressWarnings("unchecked")
1219 //    public static void setLog4jLogLevel(org.apache.log4j.Level newLevel)
1220 //    {
1221 //        Logger rootLogger = org.apache.log4j.Logger.getRootLogger();
1222 //        Enumeration<Logger> enLogger = rootLogger.getLoggerRepository().getCurrentLoggers();
1223 //        Logger tmpLogger = null;
1224 //        /* If logger is root, then need to loop through all loggers under root
1225 //        * and change their logging levels too.  Also, skip sql loggers so
1226 //        they
1227 //        * do not get effected.
1228 //        */
1229 //        while(enLogger.hasMoreElements())
1230 //        {
1231 //            tmpLogger = (Logger)(enLogger.nextElement());
1232 //            tmpLogger.setLevel(newLevel);
1233 //        }
1234 //        Enumeration<Appender> enAppenders = rootLogger.getAllAppenders();
1235 //        Appender appender;
1236 //        while(enAppenders.hasMoreElements())
1237 //        {
1238 //            appender = (Appender)enAppenders.nextElement();
1239 //
1240 //            if(appender instanceof AsyncAppender)
1241 //            {
1242 //                AsyncAppender asyncAppender = (AsyncAppender)appender;
1243 //                asyncAppender.activateOptions();
1244 ////                    rfa = (RollingFileAppender)asyncAppender.getAppender("R");
1245 ////                    rfa.activateOptions();
1246 ////                    ca = (ConsoleAppender)asyncAppender.getAppender("STDOUT");
1247 ////                    ca.activateOptions();
1248 //            }
1249 //        }
1250 //
1251 //    }
1252 
1253 
1254 }