Java tutorial
/* * Copyright (C) 2009 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package cd.education.data.collector.android.utilities; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; import org.apache.commons.io.IOUtils; import org.javarosa.xform.parse.XFormParser; import org.kxml2.kdom.Document; import org.kxml2.kdom.Element; import org.kxml2.kdom.Node; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import cd.education.data.collector.android.R; import cd.education.data.collector.android.application.Collect; /** * Static methods used for common file operations. * * @author Carl Hartung (carlhartung@gmail.com) */ public class FileUtils { private final static String t = "FileUtils"; // Used to validate and display valid form names. public static final String VALID_FILENAME = "[ _\\-A-Za-z0-9]*.x[ht]*ml"; public static final String FORMID = "formid"; public static final String VERSION = "version"; // arbitrary string in OpenRosa 1.0 public static final String TITLE = "title"; public static final String SUBMISSIONURI = "submission"; public static final String BASE64_RSA_PUBLIC_KEY = "base64RsaPublicKey"; public static boolean createFolder(String path) { boolean made = true; File dir = new File(path); if (!dir.exists()) { made = dir.mkdirs(); } return made; } public static byte[] getFileAsBytes(File file) { byte[] bytes = null; InputStream is = null; try { is = new FileInputStream(file); // Get the size of the file long length = file.length(); if (length > Integer.MAX_VALUE) { Log.e(t, "File " + file.getName() + "is too large"); return null; } // Create the byte array to hold the data bytes = new byte[(int) length]; // Read in the bytes int offset = 0; int read = 0; try { while (offset < bytes.length && read >= 0) { read = is.read(bytes, offset, bytes.length - offset); offset += read; } } catch (IOException e) { Log.e(t, "Cannot read " + file.getName()); e.printStackTrace(); return null; } // Ensure all the bytes have been read in if (offset < bytes.length) { try { throw new IOException("Could not completely read file " + file.getName()); } catch (IOException e) { e.printStackTrace(); return null; } } return bytes; } catch (FileNotFoundException e) { Log.e(t, "Cannot find " + file.getName()); e.printStackTrace(); return null; } finally { // Close the input stream try { is.close(); } catch (IOException e) { Log.e(t, "Cannot close input stream for " + file.getName()); e.printStackTrace(); return null; } } } public static String getMd5Hash(File file) { try { // CTS (6/15/2010) : stream file through digest instead of handing it the byte[] MessageDigest md = MessageDigest.getInstance("MD5"); int chunkSize = 256; byte[] chunk = new byte[chunkSize]; // Get the size of the file long lLength = file.length(); if (lLength > Integer.MAX_VALUE) { Log.e(t, "File " + file.getName() + "is too large"); return null; } int length = (int) lLength; InputStream is = null; is = new FileInputStream(file); int l = 0; for (l = 0; l + chunkSize < length; l += chunkSize) { is.read(chunk, 0, chunkSize); md.update(chunk, 0, chunkSize); } int remaining = length - l; if (remaining > 0) { is.read(chunk, 0, remaining); md.update(chunk, 0, remaining); } byte[] messageDigest = md.digest(); BigInteger number = new BigInteger(1, messageDigest); String md5 = number.toString(16); while (md5.length() < 32) md5 = "0" + md5; is.close(); return md5; } catch (NoSuchAlgorithmException e) { Log.e("MD5", e.getMessage()); return null; } catch (FileNotFoundException e) { Log.e("No Cache File", e.getMessage()); return null; } catch (IOException e) { Log.e("Problem reading from file", e.getMessage()); return null; } } public static Bitmap getBitmapScaledToDisplay(File f, int screenHeight, int screenWidth) { // Determine image size of f BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeFile(f.getAbsolutePath(), o); int heightScale = o.outHeight / screenHeight; int widthScale = o.outWidth / screenWidth; // Powers of 2 work faster, sometimes, according to the doc. // We're just doing closest size that still fills the screen. int scale = Math.max(widthScale, heightScale); // get bitmap with scale ( < 1 is the same as 1) BitmapFactory.Options options = new BitmapFactory.Options(); options.inInputShareable = true; options.inPurgeable = true; options.inSampleSize = scale; Bitmap b = BitmapFactory.decodeFile(f.getAbsolutePath(), options); if (b != null) { Log.i(t, "Screen is " + screenHeight + "x" + screenWidth + ". Image has been scaled down by " + scale + " to " + b.getHeight() + "x" + b.getWidth()); } return b; } public static String copyFile(File sourceFile, File destFile) { if (sourceFile.exists()) { String errorMessage = actualCopy(sourceFile, destFile); if (errorMessage != null) { try { Thread.sleep(500); Log.e(t, "Retrying to copy the file after 500ms: " + sourceFile.getAbsolutePath()); errorMessage = actualCopy(sourceFile, destFile); } catch (InterruptedException e) { Log.e(t, e.getMessage(), e); } } return errorMessage; } else { String msg = "Source file does not exist: " + sourceFile.getAbsolutePath(); Log.e(t, msg); return msg; } } private static String actualCopy(File sourceFile, File destFile) { FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; FileChannel src = null; FileChannel dst = null; try { fileInputStream = new FileInputStream(sourceFile); src = fileInputStream.getChannel(); fileOutputStream = new FileOutputStream(destFile); dst = fileOutputStream.getChannel(); dst.transferFrom(src, 0, src.size()); dst.force(true); return null; } catch (FileNotFoundException e) { Log.e(t, "FileNotFoundException while copying file", e); return e.getMessage(); } catch (IOException e) { Log.e(t, "IOException while copying file", e); return e.getMessage(); } catch (Exception e) { Log.e(t, "Exception while copying file", e); return e.getMessage(); } finally { IOUtils.closeQuietly(fileInputStream); IOUtils.closeQuietly(fileOutputStream); IOUtils.closeQuietly(src); IOUtils.closeQuietly(dst); } } public static HashMap<String, String> parseXML(File xmlFile) { HashMap<String, String> fields = new HashMap<String, String>(); InputStream is; try { is = new FileInputStream(xmlFile); } catch (FileNotFoundException e1) { throw new IllegalStateException(e1); } InputStreamReader isr; try { isr = new InputStreamReader(is, "UTF-8"); } catch (UnsupportedEncodingException uee) { Log.w(t, "UTF 8 encoding unavailable, trying default encoding"); isr = new InputStreamReader(is); } if (isr != null) { Document doc; try { doc = XFormParser.getXMLDocument(isr); } catch (IOException e) { e.printStackTrace(); throw new IllegalStateException("Unable to parse XML document", e); } finally { try { isr.close(); } catch (IOException e) { Log.w(t, xmlFile.getAbsolutePath() + " Error closing form reader"); e.printStackTrace(); } } String xforms = "http://www.w3.org/2002/xforms"; String html = doc.getRootElement().getNamespace(); Element head = doc.getRootElement().getElement(html, "head"); Element title = head.getElement(html, "title"); if (title != null) { fields.put(TITLE, XFormParser.getXMLText(title, true)); } Element model = getChildElement(head, "model"); Element cur = getChildElement(model, "instance"); int idx = cur.getChildCount(); int i; for (i = 0; i < idx; ++i) { if (cur.isText(i)) continue; if (cur.getType(i) == Node.ELEMENT) { break; } } if (i < idx) { cur = cur.getElement(i); // this is the first data element String id = cur.getAttributeValue(null, "id"); String xmlns = cur.getNamespace(); String version = cur.getAttributeValue(null, "version"); String uiVersion = cur.getAttributeValue(null, "uiVersion"); if (uiVersion != null) { // pre-OpenRosa 1.0 variant of spec Log.e(t, "Obsolete use of uiVersion -- IGNORED -- only using version: " + version); } fields.put(FORMID, (id == null) ? xmlns : id); fields.put(VERSION, (version == null) ? null : version); } else { throw new IllegalStateException(xmlFile.getAbsolutePath() + " could not be parsed"); } try { Element submission = model.getElement(xforms, "submission"); String submissionUri = submission.getAttributeValue(null, "action"); fields.put(SUBMISSIONURI, (submissionUri == null) ? null : submissionUri); String base64RsaPublicKey = submission.getAttributeValue(null, "base64RsaPublicKey"); fields.put(BASE64_RSA_PUBLIC_KEY, (base64RsaPublicKey == null || base64RsaPublicKey.trim().length() == 0) ? null : base64RsaPublicKey.trim()); } catch (Exception e) { Log.i(t, xmlFile.getAbsolutePath() + " does not have a submission element"); // and that's totally fine. } } return fields; } // needed because element.getelement fails when there are attributes private static Element getChildElement(Element parent, String childName) { Element e = null; int c = parent.getChildCount(); int i = 0; for (i = 0; i < c; i++) { if (parent.getType(i) == Node.ELEMENT) { if (parent.getElement(i).getName().equalsIgnoreCase(childName)) { return parent.getElement(i); } } } return e; } public static void deleteAndReport(File file) { if (file != null && file.exists()) { // remove garbage if (!file.delete()) { Log.w(t, file.getAbsolutePath() + " will be deleted upon exit."); file.deleteOnExit(); } else { Log.w(t, file.getAbsolutePath() + " has been deleted."); } } } public static String constructMediaPath(String formFilePath) { String pathNoExtension = formFilePath.substring(0, formFilePath.lastIndexOf(".")); return pathNoExtension + "-media"; } /** * @param mediaDir the media folder */ public static void checkMediaPath(File mediaDir) { if (mediaDir.exists() && mediaDir.isFile()) { Log.e(t, "The media folder is already there and it is a FILE!! We will need to delete it and create a folder instead"); boolean deleted = mediaDir.delete(); if (!deleted) { throw new RuntimeException(Collect.getInstance() .getString(R.string.fs_delete_media_path_if_file_error, mediaDir.getAbsolutePath())); } } // the directory case boolean createdOrExisted = createFolder(mediaDir.getAbsolutePath()); if (!createdOrExisted) { throw new RuntimeException(Collect.getInstance().getString(R.string.fs_create_media_folder_error, mediaDir.getAbsolutePath())); } } public static void purgeMediaPath(String mediaPath) { File tempMediaFolder = new File(mediaPath); File[] tempMediaFiles = tempMediaFolder.listFiles(); if (tempMediaFiles == null || tempMediaFiles.length == 0) { deleteAndReport(tempMediaFolder); } else { for (File tempMediaFile : tempMediaFiles) { deleteAndReport(tempMediaFile); } } } public static void moveMediaFiles(String tempMediaPath, File formMediaPath) throws IOException { File tempMediaFolder = new File(tempMediaPath); File[] mediaFiles = tempMediaFolder.listFiles(); if (mediaFiles == null || mediaFiles.length == 0) { deleteAndReport(tempMediaFolder); } else { for (File mediaFile : mediaFiles) { org.apache.commons.io.FileUtils.moveFileToDirectory(mediaFile, formMediaPath, true); } deleteAndReport(tempMediaFolder); } } }