/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.das2.util; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.das2.util.filesystem.Glob; /** * static utility methods. * * introduced Jul 28, 2008. * * @author jbf */ public class FileUtil { final static Logger logger= LoggerManager.getLogger("das2.util"); /** * returns True if the file contents are equal. * @param file1 * @param file2 * @return true if the two files have identical */ public static boolean fileCompare(File file1, File file2) { if ( file1.length()!=file2.length() ) { return false; } try ( FileInputStream b1= new FileInputStream(file1); FileInputStream b2= new FileInputStream(file2) ) { long l= file1.length(); for ( long i=0; i exclude ) throws IllegalArgumentException { if (!root.exists()) { return true; } if (!root.canRead()) { throw new IllegalArgumentException("cannot read folder " + root ); } File[] children = root.listFiles(); // root is known to exist. if ( children==null ) { throw new IllegalArgumentException("listFiles returns null, root must be a directory and not a file."); } boolean success = true; boolean noExclude= true; for (File children1 : children) { if (exclude!=null && exclude.contains(children1.getName())) { noExclude= false; continue; } if (children1.isDirectory()) { success = success && deleteFileTree(children1, exclude); } else { success = success && (!children1.exists() || children1.delete()); // in case file is deleted by another process, check exists again. if (!success) { throw new IllegalArgumentException("unable to delete file " + children1); } } } if ( noExclude ) { success = success && (!root.exists() || root.delete()); } if (!success) { throw new IllegalArgumentException("unable to delete folder " + root); } return success; } /** * deletes all files with the given name, and root, just as "find . -name name -exec rm {} \;" would. * TODO: check links. For example deleteWithinFileTree( root, ".listing" ) * @param root the root directory of the tree. * @param name the file name. * @throws IllegalArgumentException if it is unable to delete a file * @return true if the operation was successful. */ public static boolean deleteWithinFileTree(File root,String name) throws IllegalArgumentException { if (!root.exists()) { return true; } if ( !root.canRead() ) { System.err.println("unable to read folder: "+root ); return true; } File[] children = root.listFiles(); if ( children==null ) { throw new IllegalArgumentException("listFiles returns null, root must be a directory and not a file."); } boolean success = true; for (File children1 : children) { if (children1.isDirectory()) { success = success && deleteWithinFileTree(children1, name); } else { if (children1.getName().equals(name)) { success = success && (!children1.exists() || children1.delete()); if (!success) { throw new IllegalArgumentException("unable to delete file " + children1); } } } } return success; } /** * find a files with the given name within the given root, just as "find . -name name -print \;" would. * TODO: check links. For example, find( "/usr/share/fonts/truetype", "FreeMono.ttf" ) * @param root the root to start * @param name name to look for. * @throws IllegalArgumentException if the root does not exist. * @return the File found, or null if it does not exist. */ public static File find(File root,String name) throws IllegalArgumentException { if (!root.exists()) { throw new IllegalArgumentException("File does not exist:"+root); } if ( !root.isDirectory() ) { throw new IllegalArgumentException("root should be a directory: "+root); } if ( !root.canRead() ) { throw new IllegalArgumentException("unable to read root: "+root); } File[] children = root.listFiles(); if ( children==null ) throw new IllegalArgumentException("should not happen because it is known to exist."); for (File children1 : children) { if (children1.isDirectory()) { File f = find(children1, name); if ( f!=null ) return f; } else { if (children1.getName().equals(name)) { return children1; } } } return null; } /** * find a files with the given pattern within the given root, * just as "find . -name *.dat -print \;" would. * TODO: check links. For example, find( "/usr/share/fonts/truetype", "FreeMono.ttf" ) * @param root the root to start * @param pattern name to look for. * @param result results are added to this list, or null if the count that is all that is needed. * @throws IllegalArgumentException if the root does not exist. * @return the number of files found * @see org.das2.util.filesystem.Glob */ public static int find( File root, Pattern pattern, List result ) throws IllegalArgumentException { if ( result==null ) result= new ArrayList<>(); if (!root.exists()) { throw new IllegalArgumentException("File does not exist:"+root); } if ( !root.isDirectory() ) { throw new IllegalArgumentException("root should be a directory: "+root); } if ( !root.canRead() ) { throw new IllegalArgumentException("unable to read root: "+root); } int count= 0; File[] children = root.listFiles(); if ( children==null ) throw new IllegalArgumentException("should not happen because it is known to exist."); for (File children1 : children) { if (children1.isDirectory()) { count += find(children1, pattern,result); } else { if ( pattern.matcher( children1.getName() ).matches() ) { result.add( children1.getAbsolutePath() ); count= count+1; } } } return count; } /** * find a files with the given name within one of the given roots. * TODO: check links. For example, find( "/usr/share/fonts/truetype", "FreeMono.ttf" ) * This allows root folders that do not exist. * @param roots array of roots to start search. * @param name name to look for. * @return the File found, or null if it does not exist. */ public static File find(File[] roots,String name) { for ( File root: roots ) { if ( root.exists() ) { File r= find( root, name ); if ( r!=null ) { return r; } } } return null; } /** * find all files under the root matching the spec. * @param root the root of the search (e.g. /fonts/) * @param name the pattern to match * @param matches list that will accept the matches, or null if one should be created. * @return the list. */ public static List listRecursively( File root, Pattern name, List matches ) { if (!root.exists()) { throw new IllegalArgumentException("File does not exist:"+root); } if (!root.isDirectory()) { throw new IllegalArgumentException("root is not a folder:"+root); } if (!root.canRead()) return Collections.emptyList(); if ( matches==null ) matches= new ArrayList(); File[] children = root.listFiles(); if ( children==null ) return Collections.emptyList(); // should not happen because it is known to exist. for (File children1 : children) { if (children1.isDirectory()) { listRecursively(children1, name, matches); } else { if (name.matcher(children1.getName()).matches()) { matches.add(children1); } } } return matches; } /** * Return an array of files where the regex is found at the end. A check is performed to see if the root is case-insensitive. * @param root the root of the search (e.g. /fonts/) * @param glob the glob to match (e.g. *.ttf) * @return list of files. */ public static File[] listRecursively( File root, String glob ) { String regex= Glob.getRegex( glob ); boolean b= new File(root,"xxx").equals(new File(root,"XXX")); if ( b ) regex= "(?i)"+regex; Pattern name= Pattern.compile( ".*" + regex ); List result= listRecursively( root, name, null ); return result.toArray( new File[result.size()] ); } /** * copies the file or folder from src to dst. * @param src the source file or folder. * @param dst the location for the new file or folder. * @throws FileNotFoundException * @throws IOException */ public static void fileCopy( File src, File dst ) throws FileNotFoundException, IOException { if ( !src.exists() ) throw new IllegalArgumentException("src does not exist."); if ( !src.canRead() ) throw new IllegalArgumentException("src cannot be read."); if ( src.equals(dst) ) throw new IllegalArgumentException("src and dst files are the same"); if ( src.isDirectory() && ( !dst.exists() || dst.isDirectory() ) ) { if ( !dst.exists() ) { if ( !dst.mkdirs() ) throw new IOException("unable to mkdir " + dst); } File dst1= new File( dst, src.getName() ); if ( !dst1.exists() && !dst1.mkdir() ) throw new IOException("unable to mkdir " + dst1); dst= dst1; File[] files= src.listFiles(); if ( files==null ) return; // should not happen because it is known to exist. for ( File f:files ) { if ( f.isDirectory() ) { dst1= new File( dst, f.getName() ); if ( !dst1.exists() && !dst1.mkdir() ) throw new IOException("unable to mkdir " + dst1); } else { dst1= dst; } fileCopy( f, dst1 ); } return; } else if ( dst.isDirectory() ) { dst= new File( dst, src.getName() ); } FileChannel ic = new FileInputStream(src).getChannel(); FileChannel oc = new FileOutputStream(dst).getChannel(); try { ic.transferTo(0, ic.size(), oc); } finally { ic.close(); oc.close(); } } /** * return the first four bytes of the file as a string. Some magic * numbers we care about: *
  • '\x89HDF' HDF (and new NetCDF) files * @param src * @return a four byte string * @throws java.io.FileNotFoundException * @throws IllegalArgumentException if the file is less than four bytes. */ public static String getMagic(File src) throws FileNotFoundException, IOException { byte[] four= new byte[4]; FileChannel ic = new FileInputStream(src).getChannel(); ByteBuffer buf= ByteBuffer.wrap(four); int bytesRead=0; try { while ( bytesRead<4 ) { int bytes= ic.read( buf ); if ( bytes==-1 ) { if ( bytesRead==0 ) { throw new IllegalArgumentException("File is empty: "+src); } else { throw new IllegalArgumentException("File has less than four bytes: "+src); } } bytesRead+= bytes; } } finally { ic.close(); } return new String( four ); } /** * read all the bytes in the UTF-8 encoded inputStream into a string. * @param ins the input stream * @return string containing the contents of the file. * @throws java.io.IOException */ public static String readInputStreamToString( InputStream ins ) throws IOException { String result = new BufferedReader(new InputStreamReader(ins)) .lines().collect(Collectors.joining("\n")); return result; } /** * read all the bytes in the UTF-8 encoded file into a string. * @param f the file, which presumed to be UTF-8 (or ASCII) encoded. * @return string containing the contents of the file. * @throws java.io.IOException */ public static String readFileToString( File f ) throws IOException { byte[] bb= Files.readAllBytes( Paths.get( f.toURI() ) ); return new String( bb, Charset.forName("UTF-8") ); } /** * write all the bytes in the string to a file using UTF-8 encoding. * @param f the file name. * @param src the string to write to the file. * @throws IOException */ public static void writeStringToFile( File f, String src ) throws IOException { try { Files.write( Paths.get( f.toURI() ), src.getBytes("UTF-8"), new OpenOption[0] ); } catch (UnsupportedEncodingException ex) { logger.log(Level.SEVERE, null, ex); } } /** * return the number of lines in the text file. Breaking the file into * lines is handled by Java's BufferedReader. * @param f the file * @return the number of lines. * @throws FileNotFoundException * @throws IOException * @see BufferedReader */ public static int lineCount( File f ) throws FileNotFoundException, IOException { try ( BufferedReader r= new BufferedReader( new FileReader( f ) ) ) { String line= r.readLine(); int lineCount=0; while ( line!=null ) { lineCount++; line= r.readLine(); } return lineCount; } } /** * read all the bytes off the stream, perhaps to empty a URL response. This * does not close the stream! * @param in the input stream, which will not be closed by this method. * @return the total number of bytes read. * @throws IOException */ public static int consumeStream( InputStream in ) throws IOException { byte[] buf= new byte[2048]; int totalBytesRead=0; int bytesRead= in.read( buf, 0, buf.length ); while ( bytesRead>-1 ) { totalBytesRead += bytesRead; bytesRead= in.read( buf, 0, buf.length ); } return totalBytesRead; } }