001/**
002 * Copyright 2010-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.common.util;
017
018import java.io.BufferedReader;
019import java.io.BufferedWriter;
020import java.io.File;
021import java.io.FileWriter;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.InputStreamReader;
025import java.io.OutputStream;
026import java.io.OutputStreamWriter;
027import java.io.PrintStream;
028import java.io.Reader;
029import java.io.StringReader;
030import java.io.Writer;
031import java.net.MalformedURLException;
032import java.net.URI;
033import java.net.URISyntaxException;
034import java.net.URL;
035import java.security.MessageDigest;
036import java.security.NoSuchAlgorithmException;
037import java.util.ArrayList;
038import java.util.Arrays;
039import java.util.Collections;
040import java.util.List;
041import java.util.Properties;
042
043import org.apache.commons.io.FileUtils;
044import org.apache.commons.io.IOUtils;
045import org.apache.commons.lang3.StringUtils;
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048import org.springframework.core.io.DefaultResourceLoader;
049import org.springframework.core.io.Resource;
050import org.springframework.core.io.ResourceLoader;
051import org.springframework.util.Assert;
052
053public class LocationUtils {
054
055        private static final Logger logger = LoggerFactory.getLogger(LocationUtils.class);
056
057        private static final String FILE_PREFIX = "file:";
058        private static final String BACK_SLASH = "\\";
059        private static final String FORWARD_SLASH = "/";
060        private static final String SLASH_DOT_SLASH = "/./";
061        private static final String DOT_DOT_SLASH = "../";
062        private static final String SLASH_DOT_DOT = "/..";
063        private static final String CLASSPATH = "classpath:";
064        private static final String MD5 = "MD5";
065
066        /**
067         * Get the MD5 checksum of the location
068         */
069        public static String getMD5Checksum(String location) {
070                return getChecksum(location, MD5);
071        }
072
073        /**
074         * Get the MD5 checksum of the file
075         */
076        public static String getMD5Checksum(File file) {
077                return getChecksum(getCanonicalPath(file), MD5);
078        }
079
080        /**
081         * Open a <code>PrintStream</code> to the indicated file. Parent directories are created if necessary.
082         */
083        public static final PrintStream openPrintStream(File file) throws IOException {
084                return new PrintStream(FileUtils.openOutputStream(file));
085        }
086
087        /**
088         * Open a <code>Writer</code> to the indicated file. Parent directories are created if necessary.
089         */
090        public static final Writer openWriter(File file) throws IOException {
091                touch(file);
092                return new FileWriter(file);
093        }
094
095        /**
096         * Open a <code>Writer</code> to the <code>location</code> (It must be a writable file on the local file system). Parent directories are created if necessary.
097         */
098        public static final Writer openWriter(String location) throws IOException {
099                return openWriter(new File(location));
100        }
101
102        public static Properties getLocationProperties(LocationPropertiesContext context) {
103
104                Assert.notNull(context, "context is null");
105
106                Properties properties = context.getProperties();
107                String keySuffix = context.getKeySuffix();
108                String locationPropertiesSuffix = context.getLocationPropertiesSuffix();
109                String encoding = context.getEncoding();
110
111                Assert.notNull(properties, "properties is null");
112                Assert.notNull(keySuffix, "keySuffix is null");
113                Assert.notNull(locationPropertiesSuffix, "locationPropertiesSuffix is null");
114
115                List<String> keys = PropertyUtils.getEndsWithKeys(properties, keySuffix);
116
117                Properties locationProperties = new Properties();
118                for (String key : keys) {
119                        String location = properties.getProperty(key);
120                        if (!exists(location)) {
121                                continue;
122                        }
123                        String propertiesLocation = location + locationPropertiesSuffix;
124                        if (!exists(propertiesLocation)) {
125                                continue;
126                        }
127                        Properties p = PropertyUtils.load(propertiesLocation, encoding);
128                        locationProperties.putAll(p);
129                }
130                logger.info("Located {} properties for {} location listings", locationProperties.size(), keys.size());
131                return locationProperties;
132        }
133
134        public static TextMetaData getTextMetaData(File file) {
135                return getTextMetaData(getCanonicalPath(file));
136        }
137
138        public static TextMetaData getTextMetaData(String location) {
139                long lines = 0;
140                long size = 0;
141                BufferedReader in = null;
142                try {
143                        in = getBufferedReader(location);
144                        String s = in.readLine();
145                        while (s != null) {
146                                lines++;
147                                size += s.length();
148                                s = in.readLine();
149                        }
150                        return new TextMetaData(lines, size);
151                } catch (IOException e) {
152                        throw new IllegalStateException(e);
153                } finally {
154                        IOUtils.closeQuietly(in);
155                }
156        }
157
158        /**
159         * Count the lines of text in the file.
160         * 
161         * @param file
162         *            The file to read lines of text from
163         * @return A count representing the number of lines of text
164         * @throws IllegalStateException
165         *             If there is an i/o exception reading the file
166         * @see BufferedReader
167         */
168        public static long getLineCount(File file) {
169                return getLineCount(file, null);
170        }
171
172        public static long getLineCount(File file, String encoding) {
173                return getLineCount(getCanonicalPath(file));
174        }
175
176        /**
177         * Count the lines of text in the location.
178         * 
179         * @param location
180         *            The location to read lines of text from
181         * @return A count representing the number of lines of text
182         * @throws IllegalStateException
183         *             If there is an i/o exception reading the file
184         * @see BufferedReader
185         * @deprecated Use getLineCount(location,encoding) instead
186         */
187        @Deprecated
188        public static long getLineCount(String location) {
189                return getLineCount(location, null);
190        }
191
192        /**
193         * Count the lines of text in the location.
194         * 
195         * @param location
196         *            The location to read lines of text from
197         * @return A count representing the number of lines of text
198         * @throws IllegalStateException
199         *             If there is an i/o exception reading the file
200         * @see BufferedReader
201         */
202        public static long getLineCount(String location, String encoding) {
203                long count = 0;
204                BufferedReader in = null;
205                try {
206                        in = getBufferedReader(location, encoding);
207                        while (in.readLine() != null) {
208                                count++;
209                        }
210                        return count;
211                } catch (IOException e) {
212                        throw new IllegalStateException("Unexpected IO error", e);
213                } finally {
214                        IOUtils.closeQuietly(in);
215                }
216        }
217
218        public static final void copyLocationsToFiles(List<String> locations, List<File> files) {
219                Assert.isTrue(locations.size() == files.size());
220                for (int i = 0; i < locations.size(); i++) {
221                        String location = locations.get(i);
222                        File destination = files.get(i);
223                        copyLocationToFile(location, destination);
224                }
225        }
226
227        /**
228         * Return the text that appears after <code>classpath:</code>. Throws <code>IllegalArgumentException</code> if location does not start with <code>classpath:</code>
229         */
230        public static final String getClasspathFilename(String location) {
231                return getClasspathFilenames(Arrays.asList(location)).get(0);
232        }
233
234        /**
235         * Return the text that appears after <code>classpath:</code>. Throws <code>IllegalArgumentException</code> if any locations do not start with <code>classpath:</code>
236         */
237        public static final List<String> getClasspathFilenames(List<String> locations) {
238                List<String> classpathFilenames = new ArrayList<String>();
239                for (String location : locations) {
240                        if (!isClasspathLocation(location)) {
241                                throw new IllegalArgumentException(location + " must start with " + CLASSPATH);
242                        } else {
243                                classpathFilenames.add(StringUtils.substring(location, CLASSPATH.length()));
244                        }
245                }
246                return classpathFilenames;
247        }
248
249        /**
250         * Return <code>true</code> if location starts with <code>classpath:</code>
251         */
252        public static final boolean isClasspathLocation(String location) {
253                return StringUtils.startsWith(location, CLASSPATH);
254        }
255
256        public static final List<String> getNormalizedPathFragments(String absolutePath, boolean directory) {
257                String normalized = getNormalizedAbsolutePath(absolutePath);
258                String[] tokens = StringUtils.split(normalized, FORWARD_SLASH);
259                List<String> fragments = new ArrayList<String>();
260                StringBuilder sb = new StringBuilder();
261                sb.append(FORWARD_SLASH);
262                int length = directory ? tokens.length : tokens.length - 1;
263                for (int i = 0; i < length; i++) {
264                        if (i != 0) {
265                                sb.append(FORWARD_SLASH);
266                        }
267                        sb.append(tokens[i]);
268                        fragments.add(sb.toString());
269                }
270                return fragments;
271        }
272
273        public static final List<String> getCanonicalPaths(List<File> files) {
274                List<String> paths = new ArrayList<String>();
275                for (File file : files) {
276                        String path = getCanonicalPath(file);
277                        paths.add(path);
278                }
279                return paths;
280        }
281
282        public static final List<String> getLocations(String location, LocationType type, String encoding) {
283                switch (type) {
284                case LOCATION:
285                        return Collections.singletonList(location);
286                case LOCATIONLIST:
287                        return getLocations(location, encoding);
288                default:
289                        throw new IllegalArgumentException("Location type '" + type + "' is unknown");
290                }
291        }
292
293        public static final List<String> getLocations(String location, LocationType type) {
294                return getLocations(location, type, null);
295        }
296
297        public static final List<String> getLocations(String locationListing) {
298                return getLocations(Collections.singletonList(locationListing), null);
299        }
300
301        public static final List<String> getLocations(String locationListing, String encoding) {
302                return getLocations(Collections.singletonList(locationListing), encoding);
303        }
304
305        public static final List<String> getLocations(List<String> locationListings) {
306                return getLocations(locationListings, null);
307        }
308
309        public static final void copyLocationToFile(String location, File destination) {
310                Assert.notNull(location);
311                Assert.notNull(destination);
312                logger.debug("Copying [{}]->[{}]", location, destination);
313                InputStream in = null;
314                try {
315                        in = getInputStream(location);
316                        FileUtils.copyInputStreamToFile(in, destination);
317                } catch (IOException e) {
318                        throw new IllegalStateException(e);
319                } finally {
320                        IOUtils.closeQuietly(in);
321                }
322        }
323
324        public static final List<File> getFiles(File dir, List<String> filenames) {
325                List<File> files = new ArrayList<File>();
326                for (String filename : filenames) {
327                        File file = new File(dir, filename);
328                        files.add(file);
329                }
330                return files;
331        }
332
333        public static final List<String> getFilenames(List<String> locations) {
334                Assert.notNull(locations);
335                List<String> filenames = new ArrayList<String>();
336                for (String location : locations) {
337                        filenames.add(getFilename(location));
338                }
339                return filenames;
340        }
341
342        /**
343         * Throw IllegalArgumentException if the locationListing does not exist, or if any of the locations inside the locationListing do not exist
344         */
345        public static final void validateLocationListing(String locationListing) {
346                validateLocationListings(Collections.singletonList(locationListing));
347        }
348
349        /**
350         * Throw IllegalArgumentException if any of the locationListings do not exist, or if any of the locations inside any of the locationListings do not exist
351         */
352        public static final void validateLocationListings(List<String> locationListings) {
353                for (String locationListing : locationListings) {
354                        validateLocation(locationListing);
355                        List<String> locations = getLocations(locationListing);
356                        validateLocations(locations);
357                }
358        }
359
360        public static final void validateLocations(List<String> locations) {
361                for (String location : locations) {
362                        validateLocation(location);
363                }
364        }
365
366        /**
367         * Throw IllegalArgumentException if the location does not exist
368         */
369        public static final void validateLocation(String location) {
370                validateLocation(location, "[" + location + "] does not exist");
371        }
372
373        /**
374         * Throw IllegalArgumentException if the location does not exist
375         */
376        public static final void validateLocation(String location, String message) {
377                Assert.isTrue(exists(location), message);
378        }
379
380        public static final List<String> getLocations(List<String> locationListings, String encoding) {
381                List<String> locations = new ArrayList<String>();
382                for (String locationListing : locationListings) {
383                        List<String> lines = readLines(locationListing, encoding);
384                        locations.addAll(lines);
385                }
386                return locations;
387        }
388
389        public static final String getCanonicalURLString(File file) {
390                if (file == null) {
391                        return null;
392                }
393                String path = getCanonicalPath(file);
394                File canonical = new File(path);
395                return getURLString(canonical);
396        }
397
398        public static final void validateNormalizedPath(String originalPath, String normalizedPath) {
399                if (CollectionUtils.containsAny(normalizedPath, Arrays.asList(SLASH_DOT_DOT, SLASH_DOT_SLASH, DOT_DOT_SLASH))) {
400                        throw new IllegalArgumentException("[" + originalPath + "] could not be normalized. Normalized path [" + normalizedPath + "]");
401                }
402        }
403
404        /**
405         * Resolve and remove <code>..</code> and <code>.</code> from <code>absolutePath</code> after converting any back slashes to forward slashes
406         */
407        public static final String getNormalizedAbsolutePath(String absolutePath) {
408                if (absolutePath == null) {
409                        return null;
410                }
411                String replaced = StringUtils.replace(absolutePath, BACK_SLASH, FORWARD_SLASH);
412                boolean absolute = StringUtils.startsWith(replaced, FORWARD_SLASH);
413                if (!absolute) {
414                        throw new IllegalArgumentException("[" + absolutePath + "] is not an absolute path.");
415                }
416                String prefixed = FILE_PREFIX + replaced;
417                try {
418                        URI rawURI = new URI(prefixed);
419                        URI normalizedURI = rawURI.normalize();
420                        URL normalizedURL = normalizedURI.toURL();
421                        String externalForm = normalizedURL.toExternalForm();
422                        String trimmed = StringUtils.substring(externalForm, FILE_PREFIX.length());
423                        validateNormalizedPath(absolutePath, trimmed);
424                        return trimmed;
425                } catch (MalformedURLException e) {
426                        throw new IllegalArgumentException(e);
427                } catch (URISyntaxException e) {
428                        throw new IllegalArgumentException(e);
429                }
430        }
431
432        public static final String getURLString(File file) {
433                if (file == null) {
434                        return null;
435                }
436                try {
437                        URI uri = file.toURI();
438                        URL url = uri.toURL();
439                        return url.toExternalForm();
440                } catch (MalformedURLException e) {
441                        throw new IllegalArgumentException(e);
442                }
443        }
444
445        public static final void forceMkdir(File file) {
446                try {
447                        FileUtils.forceMkdir(file);
448                } catch (IOException e) {
449                        throw new IllegalArgumentException("Unexpected IO error", e);
450                }
451        }
452
453        public static final void touch(File file) {
454                try {
455                        FileUtils.touch(file);
456                } catch (IOException e) {
457                        throw new IllegalArgumentException("Unexpected IO error", e);
458                }
459        }
460
461        public static final String getCanonicalPath(File file) {
462                try {
463                        return file.getCanonicalPath();
464                } catch (IOException e) {
465                        throw new IllegalArgumentException("Unexpected IO error", e);
466                }
467        }
468
469        /**
470         * Null safe method to unconditionally attempt to delete <code>filename</code> without throwing an exception. If <code>filename</code> is a directory, delete it and all
471         * sub-directories.
472         */
473        public static final boolean deleteFileQuietly(String filename) {
474                File file = getFileQuietly(filename);
475                return FileUtils.deleteQuietly(file);
476        }
477
478        /**
479         * Null safe method for getting a <code>File</code> handle from <code>filename</code>. If <code>filename</code> is null, null is returned.
480         */
481        public static final File getFileQuietly(String filename) {
482                if (filename == null) {
483                        return null;
484                } else {
485                        return new File(filename);
486                }
487        }
488
489        /**
490         * Get the contents of <code>file</code> as a <code>String</code> using the platform's default character encoding.
491         */
492        public static final String toString(File file) {
493                return toString(file, null);
494        }
495
496        /**
497         * Get the contents of <code>file</code> as a <code>String</code> using the specified character encoding.
498         */
499        public static final String toString(File file, String encoding) {
500                return toString(getCanonicalPath(file), encoding);
501        }
502
503        /**
504         * Get the contents of <code>location</code> as a <code>String</code> using the platform's default character encoding.
505         */
506        public static final String toString(String location) {
507                return toString(location, null);
508        }
509
510        /**
511         * Get the contents of <code>location</code> as a <code>String</code> using the specified character encoding.
512         */
513        public static final String toString(String location, String encoding) {
514                InputStream in = null;
515                try {
516                        in = getInputStream(location);
517                        if (encoding == null) {
518                                return IOUtils.toString(in);
519                        } else {
520                                return IOUtils.toString(in, encoding);
521                        }
522                } catch (IOException e) {
523                        throw new IllegalStateException("Unexpected IO error", e);
524                } finally {
525                        IOUtils.closeQuietly(in);
526                }
527        }
528
529        /**
530         * Get the contents of <code>s</code> as a list of <code>String's</code> one entry per line
531         */
532        public static final List<String> readLinesFromString(String s) {
533                Reader reader = getBufferedReaderFromString(s);
534                return readLinesAndClose(reader);
535        }
536
537        public static final List<String> readLinesAndClose(InputStream in) {
538                return readLinesAndClose(in, null);
539        }
540
541        public static final List<String> readLinesAndClose(InputStream in, String encoding) {
542                Reader reader = null;
543                try {
544                        reader = getBufferedReader(in, encoding);
545                        return readLinesAndClose(reader);
546                } catch (IOException e) {
547                        throw new IllegalStateException("Unexpected IO error", e);
548                } finally {
549                        IOUtils.closeQuietly(reader);
550                }
551        }
552
553        public static final List<String> readLinesAndClose(Reader reader) {
554                try {
555                        return IOUtils.readLines(reader);
556                } catch (IOException e) {
557                        throw new IllegalStateException("Unexpected IO error", e);
558                } finally {
559                        IOUtils.closeQuietly(reader);
560                }
561        }
562
563        /**
564         * Get the contents of <code>file</code> as a list of <code>String's</code> one entry per line using the platform default encoding
565         */
566        public static final List<String> readLines(File file) {
567                return readLines(getCanonicalPath(file));
568        }
569
570        /**
571         * Get the contents of <code>location</code> as a list of <code>String's</code> one entry per line using the platform default encoding
572         */
573        public static final List<String> readLines(String location) {
574                return readLines(location, null);
575        }
576
577        /**
578         * Get the contents of <code>location</code> as a list of <code>String's</code> one entry per line using the encoding indicated.
579         */
580        public static final List<String> readLines(String location, String encoding) {
581                Reader reader = null;
582                try {
583                        reader = getBufferedReader(location, encoding);
584                        return readLinesAndClose(reader);
585                } catch (IOException e) {
586                        throw new IllegalStateException("Unexpected IO error", e);
587                } finally {
588                        IOUtils.closeQuietly(reader);
589                }
590        }
591
592        /**
593         * Return a <code>BufferedReader</code> for the location indicated using the platform default encoding.
594         */
595        public static final BufferedReader getBufferedReader(String location) throws IOException {
596                return getBufferedReader(location, null);
597        }
598
599        /**
600         * Return a <code>BufferedReader</code> for the location indicated using the encoding indicated.
601         */
602        public static final BufferedReader getBufferedReader(String location, String encoding) throws IOException {
603                try {
604                        InputStream in = getInputStream(location);
605                        return getBufferedReader(in, encoding);
606                } catch (IOException e) {
607                        throw new IOException("Unexpected IO error", e);
608                }
609        }
610
611        /**
612         * Return a <code>BufferedReader</code> that reads from <code>s</code>
613         */
614        public static final BufferedReader getBufferedReaderFromString(String s) {
615                return new BufferedReader(new StringReader(s));
616        }
617
618        /**
619         * Return a <code>Writer</code> that writes to <code>out</code> using the indicated encoding. <code>null</code> means use the platform's default encoding.
620         */
621        public static final Writer getWriter(OutputStream out, String encoding) throws IOException {
622                if (encoding == null) {
623                        return new BufferedWriter(new OutputStreamWriter(out));
624                } else {
625                        return new BufferedWriter(new OutputStreamWriter(out, encoding));
626                }
627        }
628
629        /**
630         * Return a <code>BufferedReader</code> that reads from <code>file</code> using the indicated encoding. <code>null</code> means use the platform's default encoding.
631         */
632        public static final BufferedReader getBufferedReader(File file, String encoding) throws IOException {
633                return getBufferedReader(FileUtils.openInputStream(file), encoding);
634        }
635
636        /**
637         * Return a <code>BufferedReader</code> that reads from <code>in</code> using the indicated encoding. <code>null</code> means use the platform's default encoding.
638         */
639        public static final BufferedReader getBufferedReader(InputStream in, String encoding) throws IOException {
640                if (encoding == null) {
641                        return new BufferedReader(new InputStreamReader(in));
642                } else {
643                        return new BufferedReader(new InputStreamReader(in, encoding));
644                }
645        }
646
647        /**
648         * Null safe method for determining if <code>location</code> is an existing file.
649         */
650        public static final boolean isExistingFile(String location) {
651                if (location == null) {
652                        return false;
653                }
654                File file = new File(location);
655                return file.exists();
656        }
657
658        /**
659         * Null safe method for determining if <code>location</code> exists.
660         */
661        public static final boolean exists(File file) {
662                if (file == null) {
663                        return false;
664                }
665                String location = getCanonicalPath(file);
666                if (isExistingFile(location)) {
667                        return true;
668                } else {
669                        Resource resource = getResource(location);
670                        return resource.exists();
671                }
672        }
673
674        public static void validateExists(List<String> locations) {
675                StringBuilder sb = new StringBuilder();
676                for (String location : locations) {
677                        if (!LocationUtils.exists(location)) {
678                                sb.append("Location [" + location + "] does not exist\n");
679                        }
680                }
681                if (sb.length() > 0) {
682                        throw new IllegalArgumentException(sb.toString());
683                }
684        }
685
686        /**
687         * Null safe method for determining if <code>location</code> exists.
688         */
689        public static final boolean exists(String location) {
690                if (location == null) {
691                        return false;
692                }
693                if (isExistingFile(location)) {
694                        return true;
695                } else {
696                        Resource resource = getResource(location);
697                        return resource.exists();
698                }
699        }
700
701        /**
702         * Open an <code>InputStream</code> to <code>location</code>. If <code>location</code> is the path to an existing <code>File</code> on the local file system, a
703         * <code>FileInputStream</code> is returned. Otherwise Spring's resource loading framework is used to open an <code>InputStream</code> to <code>location</code>.
704         */
705        public static final InputStream getInputStream(String location) throws IOException {
706                if (isExistingFile(location)) {
707                        return FileUtils.openInputStream(new File(location));
708                }
709                Resource resource = getResource(location);
710                return resource.getInputStream();
711        }
712
713        public static final Resource getResource(String location) {
714                if (location == null) {
715                        return null;
716                }
717                ResourceLoader loader = new DefaultResourceLoader();
718                return loader.getResource(location);
719        }
720
721        public static final String getFilename(String location) {
722                if (location == null) {
723                        return null;
724                }
725                if (isExistingFile(location)) {
726                        return getFileQuietly(location).getName();
727                } else {
728                        Resource resource = getResource(location);
729                        return resource.getFilename();
730                }
731        }
732
733        public static final List<String> getAbsolutePaths(List<File> files) {
734                List<String> results = new ArrayList<String>(files.size());
735
736                for (File f : files) {
737                        results.add(f.getAbsolutePath());
738                }
739
740                return results;
741        }
742
743        public static final ComparisonResults getLocationListComparison(List<String> newLocations, List<String> originalLocations) {
744                ComparisonResults result = new ComparisonResults();
745
746                result.setAdded(new ArrayList<String>());
747                result.setSame(new ArrayList<String>());
748                result.setDeleted(new ArrayList<String>());
749
750                for (String newLocation : newLocations) {
751                        if (originalLocations.contains(newLocation)) {
752                                // if a location is in both lists, add it to the "same" list
753                                result.getSame().add(newLocation);
754                        } else {
755                                // if a location is only in the new list, add it to the "added" list
756                                result.getAdded().add(newLocation);
757                        }
758                }
759
760                // the "deleted" list will contain all locations from the original list that are NOT in the new list
761                result.getDeleted().addAll(originalLocations);
762                result.getDeleted().removeAll(newLocations);
763
764                return result;
765        }
766
767        public static String getChecksum(String location, String algorithm) {
768                byte[] bytes = createChecksum(location, algorithm);
769                return getChecksum(bytes, algorithm);
770        }
771
772        public static String getChecksum(byte[] bytes, String algorithm) {
773                StringBuilder sb = new StringBuilder();
774                for (int i = 0; i < bytes.length; i++) {
775                        sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
776                }
777                return sb.toString();
778        }
779
780        public static byte[] createChecksum(String location, String algorithm) {
781                InputStream in = null;
782                try {
783                        in = LocationUtils.getInputStream(location);
784                        return createChecksum(in, algorithm);
785                } catch (IOException e) {
786                        throw new IllegalStateException("Unexpected IO error", e);
787                } finally {
788                        IOUtils.closeQuietly(in);
789                }
790        }
791
792        public static byte[] createChecksum(InputStream in, String algorithm) throws IOException {
793                try {
794                        byte[] buffer = new byte[1024];
795                        MessageDigest complete = MessageDigest.getInstance(algorithm);
796                        int numRead;
797                        do {
798                                numRead = in.read(buffer);
799                                if (numRead > 0) {
800                                        complete.update(buffer, 0, numRead);
801                                }
802                        } while (numRead != -1);
803                        IOUtils.closeQuietly(in);
804                        return complete.digest();
805                } catch (NoSuchAlgorithmException e) {
806                        throw new IllegalStateException("Unexpected message digest error", e);
807                } catch (IOException e) {
808                        throw e;
809                }
810        }
811}