View Javadoc

1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.theme.util;
17  
18  import org.apache.commons.lang3.StringUtils;
19  import org.codehaus.plexus.util.DirectoryScanner;
20  import org.codehaus.plexus.util.FileUtils;
21  import org.codehaus.plexus.util.SelectorUtils;
22  
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileWriter;
26  import java.io.IOException;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.Properties;
30  
31  /**
32   * Utility methods for the view builder module
33   *
34   * @author Kuali Rice Team (rice.collab@kuali.org)
35   */
36  public class ThemeBuilderUtils {
37  
38      /**
39       * Retrieve the {@link Properties} object loaded from the theme.properties file found in the given
40       * theme directory
41       *
42       * @param themeDirectory directory for the theme to pull properties file from
43       * @return Properties object loaded with the theme configuration, or null if the properties file
44       *         does not exist
45       * @throws IOException
46       */
47      public static Properties retrieveThemeProperties(String themeDirectory) throws IOException {
48          Properties themeProperties = null;
49  
50          FileInputStream fileInputStream = null;
51  
52          try {
53              File propertiesFile = new File(themeDirectory, ThemeBuilderConstants.THEME_PROPERTIES_FILE);
54  
55              if (propertiesFile.exists()) {
56                  fileInputStream = new FileInputStream(propertiesFile);
57  
58                  themeProperties = new Properties();
59                  themeProperties.load(fileInputStream);
60              }
61          } finally {
62              if (fileInputStream != null) {
63                  fileInputStream.close();
64              }
65          }
66  
67          return themeProperties;
68      }
69  
70      /**
71       * Stores the given properties object in a file named <code>theme-derived.properties</code> within the
72       * given theme directory
73       *
74       * @param themeDirectory directory the properties file should be created in
75       * @param themeProperties properties that should be written to the properties file
76       * @throws IOException
77       */
78      public static void storeThemeProperties(String themeDirectory, Properties themeProperties) throws IOException {
79          File propertiesFile = new File(themeDirectory, ThemeBuilderConstants.THEME_DERIVED_PROPERTIES_FILE);
80  
81          // need to remove file if already exists so the new properties will be written
82          if (propertiesFile.exists()) {
83              FileUtils.forceDelete(propertiesFile);
84          }
85  
86          FileWriter fileWriter = null;
87  
88          try {
89              fileWriter = new FileWriter(propertiesFile);
90  
91              themeProperties.store(fileWriter, null);
92          } finally {
93              if (fileWriter != null) {
94                  fileWriter.close();
95              }
96          }
97      }
98  
99      /**
100      * Retrieves the value for the property with the given key from the properties object, as a list of
101      * strings (by splitting the value on commas)
102      *
103      * @param key key for the property to retrieve
104      * @param properties properties object to pull property from
105      * @return list of strings parsed from the property value
106      */
107     public static List<String> getPropertyValueAsList(String key, Properties properties) {
108         List<String> propertyValueList = null;
109 
110         String[] propertyValueArray = getPropertyValueAsArray(key, properties);
111         if (propertyValueArray != null) {
112             propertyValueList = new ArrayList<String>();
113 
114             for (String value : propertyValueArray) {
115                 propertyValueList.add(value);
116             }
117         }
118 
119         return propertyValueList;
120     }
121 
122     /**
123      * Retrieves the value for the property with the given key from the properties object, as an array of
124      * strings (by splitting the value on commas)
125      *
126      * @param key key for the property to retrieve
127      * @param properties properties object to pull property from
128      * @return array of strings parsed from the property value
129      */
130     public static String[] getPropertyValueAsArray(String key, Properties properties) {
131         String[] propertyValue = null;
132 
133         if (properties.containsKey(key)) {
134             String propertyValueString = properties.getProperty(key);
135 
136             if (!StringUtils.isBlank(propertyValueString)) {
137                 propertyValue = propertyValueString.split(",");
138             }
139         }
140 
141         return propertyValue;
142     }
143 
144     /**
145      * Copies the property key/value from the source properties to the target properties if a property with the
146      * same key does not exist in the target properties
147      *
148      * @param propertyKey key of the property to copy
149      * @param sourceProperties properties to pull the property from
150      * @param targetProperties properties to copy the property to
151      */
152     public static void copyProperty(String propertyKey, Properties sourceProperties, Properties targetProperties) {
153         if (targetProperties != null && targetProperties.containsKey(propertyKey) && StringUtils.isNotBlank(
154                 targetProperties.getProperty(propertyKey))) {
155             return;
156         }
157 
158         if (sourceProperties != null && sourceProperties.containsKey(propertyKey)) {
159             String propertyValue = sourceProperties.getProperty(propertyKey);
160 
161             if (targetProperties == null) {
162                 targetProperties = new Properties();
163             }
164 
165             targetProperties.put(propertyKey, propertyValue);
166         }
167     }
168 
169     /**
170      * Iterates through each file in the given list and verifies the file exists, if not a runtime
171      * exception is thrown with the provided message
172      *
173      * @param filesToValidate list of files to check existence for
174      * @param exceptionMessage message for runtime exception if a file is found that does not exist
175      */
176     public static void validateFileExistence(List<File> filesToValidate, String exceptionMessage) {
177         if (filesToValidate == null) {
178             return;
179         }
180 
181         for (File file : filesToValidate) {
182             if (!file.exists()) {
183                 throw new RuntimeException(exceptionMessage + " Path: " + file.getPath());
184             }
185         }
186     }
187 
188     /**
189      * Indicates whether there is a file with the given name within the given directory
190      *
191      * @param directory directory to check for file
192      * @param fileName name of the file to check for
193      * @return true if there is a file in the directory, false if not
194      */
195     public static boolean directoryContainsFile(File directory, String fileName) {
196         boolean containsFile = false;
197 
198         List<String> directoryContents = getDirectoryContents(directory, null, null);
199 
200         for (String directoryFile : directoryContents) {
201             String directoryFilename = FileUtils.filename(directoryFile);
202 
203             if (directoryFilename.equals(fileName)) {
204                 containsFile = true;
205             }
206         }
207 
208         return containsFile;
209     }
210 
211     /**
212      * Retrieves a list of files that are in the given directory, possibly filtered by the list of include
213      * patterns or exclude patterns
214      *
215      * @param baseDirectory directory to retrieve files from
216      * @param includes list of patterns to match against for files to include, can include Ant patterns
217      * @param excludes list of patterns to match for excluded files, can include Ant patterns
218      * @return list of files within the directory that match all given patterns
219      */
220     public static List<File> getDirectoryFiles(File baseDirectory, String[] includes, String[] excludes) {
221         List<File> directoryFiles = new ArrayList<File>();
222 
223         List<String> directoryFileNames = getDirectoryFileNames(baseDirectory, includes, excludes);
224 
225         for (String fileName : directoryFileNames) {
226             directoryFiles.add(new File(baseDirectory, fileName));
227         }
228 
229         return directoryFiles;
230     }
231 
232     /**
233      * Retrieves a list of file names that are in the given directory, possibly filtered by the list of include
234      * patterns or exclude patterns
235      *
236      * @param baseDirectory directory to retrieve file names from
237      * @param includes list of patterns to match against for file names to include, can include Ant patterns
238      * @param excludes list of patterns to match for excluded file names, can include Ant patterns
239      * @return list of file names within the directory that match all given patterns
240      */
241     public static List<String> getDirectoryFileNames(File baseDirectory, String[] includes, String[] excludes) {
242         List<String> files = new ArrayList<String>();
243 
244         DirectoryScanner scanner = new DirectoryScanner();
245 
246         if (includes != null) {
247             scanner.setIncludes(includes);
248         }
249 
250         if (excludes != null) {
251             scanner.setExcludes(excludes);
252         }
253 
254         scanner.setCaseSensitive(false);
255         scanner.addDefaultExcludes();
256         scanner.setBasedir(baseDirectory);
257 
258         scanner.scan();
259 
260         for (String includedFilename : scanner.getIncludedFiles()) {
261             files.add(includedFilename);
262         }
263 
264         return files;
265     }
266 
267     /**
268      * Get the sub directories of the given directory that have the given names
269      *
270      * @param baseDirectory directory containing the sub directories
271      * @param subDirectoryNames list of sub directory names to return
272      * @return list of Files pointing to the sub directories
273      */
274     public static List<File> getSubDirectories(File baseDirectory, List<String> subDirectoryNames) {
275         List<File> subDirs = null;
276 
277         if (subDirectoryNames != null) {
278             subDirs = new ArrayList<File>();
279 
280             for (String pluginName : subDirectoryNames) {
281                 subDirs.add(new File(baseDirectory, pluginName));
282             }
283         }
284 
285         return subDirs;
286     }
287 
288     /**
289      * Retrieves a list of files and directories that are in the given directory, possibly filtered by the
290      * list of include patterns or exclude patterns
291      *
292      * @param baseDirectory directory to retrieve files and directories from
293      * @param includes list of patterns to match against for files to include, can include Ant patterns
294      * @param excludes list of patterns to match for excluded files, can include Ant patterns
295      * @return list of files within the directory that match all given patterns
296      */
297     public static List<String> getDirectoryContents(File baseDirectory, String[] includes, String[] excludes) {
298         List<String> contents = new ArrayList<String>();
299 
300         DirectoryScanner scanner = new DirectoryScanner();
301 
302         if (includes != null) {
303             scanner.setIncludes(includes);
304         }
305 
306         if (excludes != null) {
307             scanner.setExcludes(excludes);
308         }
309 
310         scanner.setCaseSensitive(false);
311         scanner.addDefaultExcludes();
312         scanner.setBasedir(baseDirectory);
313 
314         scanner.scan();
315 
316         for (String includedDirectory : scanner.getIncludedDirectories()) {
317             contents.add(includedDirectory);
318         }
319 
320         for (String includedFilename : scanner.getIncludedFiles()) {
321             contents.add(includedFilename);
322         }
323 
324         return contents;
325     }
326 
327     /**
328      * Copies all the contents from the directory given by the source path to the directory given by the
329      * target path
330      *
331      * <p>
332      * If source directory does not exist nothing is performed. The target directory will be created if it
333      * does not exist. Any hidden directories (directory names that start with ".") will be deleted from the
334      * target directory
335      * </p>
336      *
337      * @param sourceDirectoryPath absolute path to the source directory
338      * @param targetDirectoryPath absolute path to the target directory
339      * @throws IOException
340      */
341     public static void copyDirectory(String sourceDirectoryPath, String targetDirectoryPath)
342             throws IOException {
343         File sourceDir = new File(sourceDirectoryPath);
344 
345         if (!sourceDir.exists()) {
346             return;
347         }
348 
349         File targetDir = new File(targetDirectoryPath);
350         if (targetDir.exists()) {
351             // force removal so the copy starts clean
352             FileUtils.forceDelete(targetDir);
353         }
354 
355         targetDir.mkdir();
356 
357         FileUtils.copyDirectoryStructure(sourceDir, targetDir);
358 
359         // remove hidden directories from the target
360         DirectoryScanner scanner = new DirectoryScanner();
361         scanner.setBasedir(targetDir);
362 
363         scanner.scan();
364 
365         for (String includedDirectory : scanner.getIncludedDirectories()) {
366             File subdirectory = new File(targetDir, includedDirectory);
367 
368             if (subdirectory.exists() && subdirectory.isDirectory()) {
369                 if (subdirectory.getName().startsWith(".")) {
370                     FileUtils.forceDelete(subdirectory);
371                 }
372             }
373         }
374     }
375 
376     /**
377      * Copies all content (files and directories) from the source directory to the target directory, except content
378      * that already exists in the target directory (same name and path), in other words it does not override any
379      * existing content
380      *
381      * <p>
382      * Files from the source directory can be excluded from the copying by setting one or more patterns in the
383      * source excludes list
384      * </p>
385      *
386      * @param sourceDirectory directory to copy content from
387      * @param targetDirectory directory to copy content to
388      * @param sourceExcludes list of patterns to match on for source exclusions
389      * @throws IOException
390      */
391     public static void copyMissingContent(File sourceDirectory, File targetDirectory, List<String> sourceExcludes)
392             throws IOException {
393         String[] copyExcludes = null;
394 
395         if ((sourceExcludes != null) && !sourceExcludes.isEmpty()) {
396             copyExcludes = new String[sourceExcludes.size()];
397 
398             copyExcludes = sourceExcludes.toArray(copyExcludes);
399         }
400 
401         List<String> sourceDirectoryContents = getDirectoryContents(sourceDirectory, null, copyExcludes);
402         List<String> targetDirectoryContents = getDirectoryContents(targetDirectory, null, null);
403 
404         for (String sourceContent : sourceDirectoryContents) {
405             if (targetDirectoryContents.contains(sourceContent)) {
406                 continue;
407             }
408 
409             // copy file to target
410             File sourceFile = new File(sourceDirectory, sourceContent);
411             File targetFile = new File(targetDirectory, sourceContent);
412 
413             if (sourceFile.isDirectory()) {
414                 targetFile.mkdir();
415             } else {
416                 FileUtils.copyFile(sourceFile, targetFile);
417             }
418         }
419     }
420 
421     /**
422      * Determines if one of the given patterns matches the given name, or if the include list is null
423      * or empty the file will be included
424      *
425      * @param name string to match
426      * @param includes list of string patterns to match on
427      * @return true if the name is a match, false if not
428      */
429     public static boolean inIncludeList(String name, String[] includes) {
430         if ((includes == null) || (includes.length == 0)) {
431             return true;
432         }
433 
434         for (String include : includes) {
435             if (SelectorUtils.matchPath(include, name, false)) {
436                 return true;
437             }
438         }
439 
440         return false;
441     }
442 
443     /**
444      * Determines if one of the given patterns matches the given name, or if the exclude list is null
445      * or empty the file will not be excluded
446      *
447      * @param name string to match
448      * @param excludes list of string patterns to match on
449      * @return true if the name is a match, false if not
450      */
451     public static boolean inExcludeList(String name, String[] excludes) {
452         if ((excludes == null) || (excludes.length == 0)) {
453             return false;
454         }
455 
456         for (String exclude : excludes) {
457             if (SelectorUtils.matchPath(exclude, name, false)) {
458                 return true;
459             }
460         }
461 
462         return false;
463     }
464 
465     /**
466      * Iterates through the given list of patterns and checks whether the pattern ends with the given
467      * extension or a wildcard, if not the extension is appended to the pattern
468      *
469      * @param patterns array of patterns to check and append to if necessary
470      * @param extension string extension to check for and append if necessary
471      */
472     public static void addExtensionToPatterns(String[] patterns, String extension) {
473         if (patterns == null) {
474             return;
475         }
476 
477         for (int i = 0; i < patterns.length; i++) {
478             String pattern = patterns[i];
479 
480             if (!(pattern.endsWith("*") || pattern.endsWith(extension))) {
481                 patterns[i] = pattern + extension;
482             }
483         }
484     }
485 
486     /**
487      * Builds a list of strings that hold the path from each given file relative to the parent
488      * directory
489      *
490      * @param parentDirectory directory to build path from
491      * @param files list of files to build relative paths for
492      * @return list of strings containing the relative paths
493      */
494     public static List<String> getRelativePaths(File parentDirectory, List<File> files) {
495         List<String> relativePaths = new ArrayList<String>();
496 
497         for (File file : files) {
498             relativePaths.add(getRelativePath(parentDirectory, file));
499         }
500 
501         return relativePaths;
502     }
503 
504     /**
505      * Returns the path of the given file relative to the parent directory
506      *
507      * @param parentDirectory directory to build path from
508      * @param file file to build relative paths for
509      * @return string containing the relative path
510      */
511     public static String getRelativePath(File parentDirectory, File file) {
512         String relativePath = null;
513 
514         String parentPath = parentDirectory.getPath();
515         String childPath = file.getPath();
516 
517         if (childPath.startsWith(parentPath + File.separator)) {
518             relativePath = childPath.substring(parentPath.length() + 1);
519         }
520 
521         // switch path separators
522         relativePath = relativePath.replaceAll("\\\\", "/");
523 
524         return relativePath;
525     }
526 
527     /**
528      * Calculates the path from the first file to the second
529      *
530      * <p>
531      * Assumes there is a common base directory somewhere in the path of both files. Once it finds that base
532      * directory, builds the path starting at the from file to it, then adds the path from the base directory
533      * to the target file
534      * </p>
535      *
536      * @param fromFile file whose path is the starting point
537      * @param toFile file whose path is the ending point
538      * @return string containing the path
539      */
540     public static String calculatePathToFile(File fromFile, File toFile) {
541         String pathToFile = "";
542 
543         int directoriesUp = 0;
544         String parentPath = fromFile.getParent();
545 
546         while ((parentPath != null) && !fileMatchesPath(parentPath, toFile)) {
547             File parent = new File(parentPath);
548 
549             parentPath = parent.getParent();
550             directoriesUp += 1;
551         }
552 
553         if (parentPath != null) {
554             for (int i = 0; i < directoriesUp; i++) {
555                 pathToFile += "../";
556             }
557 
558             String remainingPath = toFile.getPath().replace(parentPath, "");
559 
560             if (remainingPath.startsWith(File.separator)) {
561                 remainingPath = remainingPath.substring(1);
562             }
563 
564             // switch path separators
565             remainingPath = remainingPath.replaceAll("\\\\", "/");
566 
567             // remove file name from path
568             if (remainingPath.contains("/")) {
569                 int separatorIndex = remainingPath.lastIndexOf("/");
570                 remainingPath = remainingPath.substring(0, separatorIndex + 1);
571             } else {
572                 // file in same directory, no remaining path
573                 remainingPath = null;
574             }
575 
576             if (remainingPath != null) {
577                 pathToFile += remainingPath;
578             }
579         }
580 
581         return pathToFile;
582     }
583 
584     /**
585      * Indicates whether the given file is withing the given path (file's path starts with the given path), note
586      * this does not check whether the file exists
587      *
588      * @param path path to check for
589      * @param file file whose path should be checked
590      * @return true if the file is contained in the path, false if not
591      */
592     protected static boolean fileMatchesPath(String path, File file) {
593         return file.getPath().startsWith(path);
594     }
595 
596     /**
597      * Orders the list of plugin files and sub directory files according to the given patterns
598      *
599      * @param pluginFiles list of plugin files to order
600      * @param subDirFiles list of sub directory files to order
601      * @param loadFirstPatterns list of patterns for files that should be ordered first
602      * @param loadLastPatterns list of patterns for files that should be ordered last
603      * @param pluginLoadOrder list of patterns for ordering the plugin files
604      * @param subDirLoadOrder list of patterns for ordering the sub directory files
605      * @return list containing all of the given plugin and sub directory files ordered by the given patterns
606      */
607     public static List<File> orderFiles(List<File> pluginFiles, List<File> subDirFiles, List<String> loadFirstPatterns,
608             List<String> loadLastPatterns, List<String> pluginLoadOrder, List<String> subDirLoadOrder) {
609         List<File> orderedFiles = new ArrayList<File>();
610 
611         List<File> allThemeFiles = new ArrayList<File>();
612         if (pluginFiles != null) {
613             allThemeFiles.addAll(pluginFiles);
614         }
615 
616         if (subDirFiles != null) {
617             allThemeFiles.addAll(subDirFiles);
618         }
619 
620         // build end of the ordered list since those should take priority
621         List<File> endFiles = new ArrayList<File>();
622 
623         if (loadLastPatterns != null) {
624             for (String pattern : loadLastPatterns) {
625                 endFiles.addAll(matchFiles(allThemeFiles, pattern));
626             }
627         }
628 
629         // build beginning of the ordered list
630         if (loadFirstPatterns != null) {
631             for (String pattern : loadFirstPatterns) {
632                 List<File> matchedFiles = matchFiles(allThemeFiles, pattern);
633                 matchedFiles.removeAll(endFiles);
634 
635                 orderedFiles.addAll(matchedFiles);
636             }
637         }
638 
639         // add plugin files that have been configured to load before other plugin files
640         if (pluginLoadOrder != null) {
641             for (String pattern : pluginLoadOrder) {
642                 List<File> matchedFiles = matchFiles(pluginFiles, pattern);
643                 matchedFiles.removeAll(endFiles);
644                 matchedFiles.removeAll(orderedFiles);
645 
646                 orderedFiles.addAll(matchedFiles);
647             }
648         }
649 
650         // add remaining plugin files
651         if (pluginFiles != null) {
652             for (File pluginFile : pluginFiles) {
653                 if (!orderedFiles.contains(pluginFile) && !endFiles.contains(pluginFile)) {
654                     orderedFiles.add(pluginFile);
655                 }
656             }
657         }
658 
659         // add sub dir files that have been configured to load before other sub dir files
660         if (subDirLoadOrder != null) {
661             for (String pattern : subDirLoadOrder) {
662                 List<File> matchedFiles = matchFiles(subDirFiles, pattern);
663                 matchedFiles.removeAll(endFiles);
664                 matchedFiles.removeAll(orderedFiles);
665 
666                 orderedFiles.addAll(matchedFiles);
667             }
668         }
669 
670         // add remaining sub dir files
671         if (subDirFiles != null) {
672             for (File subDirFile : subDirFiles) {
673                 if (!orderedFiles.contains(subDirFile) && !endFiles.contains(subDirFile)) {
674                     orderedFiles.add(subDirFile);
675                 }
676             }
677         }
678 
679         // now add the end files in reverse to the ordered list
680         File[] endFileArray = new File[endFiles.size()];
681         endFileArray = endFiles.toArray(endFileArray);
682 
683         for (int i = endFileArray.length - 1; i >= 0; i--) {
684             orderedFiles.add(endFileArray[i]);
685         }
686 
687         return orderedFiles;
688     }
689 
690     /**
691      * Iterates through the list of files and returns those files whose names matches the given pattern
692      *
693      * @param filesToMatch list of files to match
694      * @param pattern pattern to match on
695      * @return list of files whose name that match the pattern
696      */
697     public static List<File> matchFiles(List<File> filesToMatch, String pattern) {
698         List<File> matchedFiles = new ArrayList<File>();
699 
700         for (File file : filesToMatch) {
701             if (isMatch(file, pattern)) {
702                 matchedFiles.add(file);
703             }
704         }
705 
706         return matchedFiles;
707     }
708 
709     /**
710      * Indicates whether the base name for the given file (name without path and the extension) matches
711      * the given pattern
712      *
713      * @param file file to match
714      * @param pattern pattern to match on
715      * @return true if the file name matches the pattern, false if not
716      */
717     public static boolean isMatch(File file, String pattern) {
718         boolean isMatch = false;
719 
720         String fileBasename = FileUtils.basename(file.getName());
721         if (fileBasename.endsWith(".")) {
722             fileBasename = fileBasename.substring(0, fileBasename.length() - 1);
723         }
724 
725         if (SelectorUtils.matchPath(pattern, fileBasename, false)) {
726             isMatch = true;
727         }
728 
729         return isMatch;
730     }
731 
732     /**
733      * Returns a list of files from the given list of files, that are contained within one of the given
734      * list of directories
735      *
736      * @param files list of files to filter
737      * @param directories list of directories to filter by
738      * @return list of files that are contained in the directories
739      */
740     public static List<File> getContainedFiles(List<File> files, List<File> directories) {
741         List<File> directoryFiles = new ArrayList<File>();
742 
743         for (File directory : directories) {
744             for (File file : files) {
745                 if (ThemeBuilderUtils.directoryContainsFile(directory, file.getName())) {
746                     directoryFiles.add(file);
747                 }
748             }
749         }
750 
751         return directoryFiles;
752     }
753 
754     /**
755      * Adds the string to the end of the array of strings, or creates a new array containing the string
756      * if the array parameter is null
757      *
758      * @param array string array to add to
759      * @param stringToAdd string to add
760      * @return array containing all the original array elements plus the string
761      */
762     public static String[] addToArray(String[] array, String stringToAdd) {
763         String[] arrayToAdd = null;
764 
765         if (stringToAdd != null) {
766             arrayToAdd = new String[1];
767             arrayToAdd[0] = stringToAdd;
768         }
769 
770         return addToArray(array, arrayToAdd);
771     }
772 
773     /**
774      * Adds the second array of strings to the end of the first array of strings, or creates a new array
775      * containing the second array elements if the first does not exist
776      *
777      * <p>
778      * Note: Can't use org.apache.commons.lang.ArrayUtils#addAll(java.lang.Object[], java.lang.Object[]) because it
779      * doesn't allow String arrays to be passed in. Latest version of ArrayUtils uses generics and does
780      * </p>
781      *
782      * @param array array to add to
783      * @param arrayToAdd array to add
784      * @return array containing all the strings from both arrays
785      */
786     public static String[] addToArray(String[] array, String[] arrayToAdd) {
787         if (array == null) {
788             return arrayToAdd;
789         } else if (arrayToAdd == null) {
790             return array;
791         }
792 
793         int combinedArrayLength = array.length + arrayToAdd.length;
794 
795         String[] combinedArray = new String[combinedArrayLength];
796 
797         for (int i = 0; i < array.length; i++) {
798             combinedArray[i] = array[i];
799         }
800 
801         for (int i = 0; i < arrayToAdd.length; i++) {
802             combinedArray[i + array.length] = arrayToAdd[i];
803         }
804 
805         return combinedArray;
806     }
807 
808     /**
809      * Builds a string formed with the name for each file in the list delimited by commas
810      *
811      * @param list list to join names for
812      * @return string containing all the file names
813      */
814     public static String joinFileList(List<File> list) {
815         String joinedString = null;
816 
817         if (list != null) {
818             joinedString = "";
819 
820             for (File file : list) {
821                 if (!"".equals(joinedString)) {
822                     joinedString += ",";
823                 }
824 
825                 joinedString += file.getName();
826             }
827         }
828 
829         return joinedString;
830     }
831 
832 }